Skip to content

Instantly share code, notes, and snippets.

@IPRIT
Created March 15, 2025 22:59
Show Gist options
  • Select an option

  • Save IPRIT/2611daa758efb3cd30e095e20eee352f to your computer and use it in GitHub Desktop.

Select an option

Save IPRIT/2611daa758efb3cd30e095e20eee352f to your computer and use it in GitHub Desktop.

Всеобъемлющее руководство по MCP-серверам

Содержание

  1. Введение в MCP
  2. Что такое MCP-серверы?
  3. Основные концепции
  4. Создание вашего первого MCP-сервера
  5. Примеры использования MCP-серверов
  6. Продвинутые возможности
  7. Лучшие практики
  8. Устранение неполадок
  9. Заключение

Введение в MCP

MCP (Model Context Protocol) — это протокол, который позволяет крупным языковым моделям (LLM) и другим интеллектуальным системам взаимодействовать с внешними сервисами и ресурсами. Представьте это как мост, который соединяет мощные ИИ-системы (такие как Claude) с внешним миром — интернетом, базами данных, API и другими источниками информации.

Почему это важно?

Представьте, что вы разговариваете с умным помощником, но он не может:

  • Проверить актуальную погоду
  • Найти что-то в интернете
  • Взаимодействовать с вашими файлами
  • Использовать калькулятор
  • Отправить email

Это существенно ограничивает его полезность. MCP решает эту проблему, позволяя ИИ-системам обращаться к внешним сервисам и расширять свои возможности.

Простая аналогия

Представьте MCP как "USB-порт для ИИ". Так же как USB-порт позволяет подключать к компьютеру разные устройства (камеры, мышки, принтеры), MCP позволяет подключать к ИИ разные инструменты и сервисы.


Что такое MCP-серверы?

MCP-сервер — это программа, которая:

  1. Предоставляет определенную функциональность (например, доступ к погоде, работу с файлами и т.д.)
  2. Говорит на языке протокола MCP, чтобы ИИ-системы могли ее использовать
  3. Работает как "мост" между ИИ-системой и внешними сервисами/API

Простая аналогия

Если представить, что ИИ — это клиент ресторана, то MCP-серверы — это официанты, которые приносят блюда (информацию/сервисы) с кухни (интернета/внешних API).

Что могут делать MCP-серверы?

MCP-серверы могут предоставлять ИИ доступ к:

  • Поиску в интернете
  • Прогнозу погоды
  • Новостям
  • Базам данных
  • Калькуляторам
  • Системным операциям
  • API сторонних сервисов (Twitter, Spotify, YouTube и т.д.)
  • И многому другому!

Основные концепции

Перед тем как мы перейдем к примерам, давайте разберемся с основными концепциями, которые нужно понимать:

1. Инструменты (Tools)

Инструменты — это функции или операции, которые MCP-сервер может выполнять. Например, "получить прогноз погоды", "найти информацию в Google", "отправить email".

Аналогия: 
Инструменты — это кнопки на пульте телевизора. 
Каждая кнопка выполняет определенное действие.

2. Ресурсы (Resources)

Ресурсы — это данные или информация, к которым MCP-сервер обеспечивает доступ. Например, текущая погода, информация о фильме, данные из базы.

Аналогия:
Ресурсы — это книги в библиотеке.
ИИ может "взять книгу" и прочитать информацию из нее.

3. Шаблоны ресурсов (Resource Templates)

Шаблоны ресурсов определяют, как можно обращаться к динамическим ресурсам. Например, шаблон weather://{город}/current позволяет запрашивать погоду для любого города.

Аналогия:
Шаблон ресурса — это как карточный каталог в библиотеке, 
который помогает найти нужную книгу по описанию.

4. URI (Uniform Resource Identifier)

URI — это уникальный идентификатор ресурса, который используется для обращения к конкретному ресурсу. Например, weather://Moscow/current.

Аналогия:
URI — это как адрес в городе. 
Он позволяет точно найти нужный "дом" (ресурс).

5. Запросы и ответы (Requests & Responses)

MCP работает по модели запрос-ответ:

  • ИИ отправляет запрос на MCP-сервер (например, "какая погода в Москве?")
  • MCP-сервер обрабатывает запрос и отправляет ответ (например, "в Москве 22°C, солнечно")
Аналогия:
Это как разговор по телефону — вы задаете вопрос, 
собеседник отвечает.

Создание вашего первого MCP-сервера

Давайте рассмотрим процесс создания простого MCP-сервера шаг за шагом. Не беспокойтесь, если вы не знаете, как программировать — мы рассмотрим общую концепцию, а потом перейдем к готовым примерам.

Шаг 1: Установка необходимых инструментов

Для создания MCP-сервера вам понадобятся:

  • Node.js (среда выполнения JavaScript)
  • npm (менеджер пакетов)
  • MCP SDK (набор инструментов для работы с MCP)

Шаг 2: Инициализация проекта

# Создаем новую директорию
mkdir my-weather-server
cd my-weather-server

# Инициализируем проект
npm init -y

# Устанавливаем MCP SDK и другие необходимые пакеты
npm install @modelcontextprotocol/sdk axios

Шаг 3: Создание простого MCP-сервера

Создадим файл index.js с базовым кодом MCP-сервера:

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

// Создаем экземпляр сервера
const server = new Server(
  {
    name: 'hello-world-server',
    version: '0.1.0',
  },
  {
    capabilities: {
      resources: {},
      tools: {},
    },
  }
);

// Добавляем обработчик ошибок
server.onerror = (error) => console.error('[MCP Error]', error);

// Запускаем сервер
const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);
console.error('Hello World MCP server running on stdio');

Шаг 4: Сборка и запуск

# Компилируем код (если необходимо)
npm run build

# Запускаем сервер
node index.js

Шаг 5: Конфигурация MCP-сервера

Чтобы ИИ-система могла использовать ваш MCP-сервер, необходимо добавить его в конфигурационный файл MCP:

{
  "mcpServers": {
    "hello-world": {
      "command": "node",
      "args": ["/путь/к/вашему/серверу/index.js"],
      "env": {}
    }
  }
}

Вот и всё! Теперь у вас есть простой MCP-сервер. Конечно, он пока ничего не делает, но это основа, на которой мы будем строить наши примеры.


Примеры использования MCP-серверов

Теперь давайте рассмотрим конкретные примеры MCP-серверов — от самых простых до сложных и специализированных.

Пример 1: "Hello World" MCP-сервер

Самый простой MCP-сервер, который просто возвращает приветствие.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';

class HelloWorldServer {
  constructor() {
    this.server = new Server(
      {
        name: 'hello-world-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'say_hello',
          description: 'Returns a friendly greeting',
          inputSchema: {
            type: 'object',
            properties: {
              name: {
                type: 'string',
                description: 'Name of the person to greet',
              }
            },
            required: ['name'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'say_hello') {
        const name = request.params.arguments.name;
        return {
          content: [
            {
              type: 'text',
              text: `Привет, ${name}! Как дела?`,
            },
          ],
        };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Hello World MCP server running on stdio');
  }
}

const server = new HelloWorldServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Поздоровайся со мной"
ИИ: [использует MCP-сервер] "Привет, [имя]! Как дела?"

Пример 2: Сервер погоды

MCP-сервер, который предоставляет информацию о погоде.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ListToolsRequestSchema,
  McpError,
  ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';

const API_KEY = process.env.OPENWEATHER_API_KEY;
if (!API_KEY) {
  throw new Error('OPENWEATHER_API_KEY environment variable is required');
}

class WeatherServer {
  constructor() {
    this.server = new Server(
      {
        name: 'weather-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.axiosInstance = axios.create({
      baseURL: 'http://api.openweathermap.org/data/2.5',
      params: {
        appid: API_KEY,
        units: 'metric',
      },
    });

    this.setupResourceHandlers();
    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupResourceHandlers() {
    this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
      resourceTemplates: [
        {
          uriTemplate: 'weather://{city}/current',
          name: 'Current weather for a given city',
          mimeType: 'application/json',
          description: 'Real-time weather data for a specified city',
        },
      ],
    }));

    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const match = request.params.uri.match(/^weather:\/\/([^/]+)\/current$/);
      if (!match) {
        throw new McpError(
          ErrorCode.InvalidRequest,
          `Invalid URI format: ${request.params.uri}`
        );
      }
      const city = decodeURIComponent(match[1]);

      try {
        const response = await this.axiosInstance.get('weather', {
          params: { q: city },
        });

        return {
          contents: [
            {
              uri: request.params.uri,
              mimeType: 'application/json',
              text: JSON.stringify(
                {
                  temperature: response.data.main.temp,
                  conditions: response.data.weather[0].description,
                  humidity: response.data.main.humidity,
                  wind_speed: response.data.wind.speed,
                  timestamp: new Date().toISOString(),
                },
                null,
                2
              ),
            },
          ],
        };
      } catch (error) {
        if (axios.isAxiosError(error)) {
          throw new McpError(
            ErrorCode.InternalError,
            `Weather API error: ${error.response?.data.message ?? error.message}`
          );
        }
        throw error;
      }
    });
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'get_forecast',
          description: 'Get weather forecast for a city',
          inputSchema: {
            type: 'object',
            properties: {
              city: {
                type: 'string',
                description: 'City name',
              },
              days: {
                type: 'number',
                description: 'Number of days (1-5)',
                minimum: 1,
                maximum: 5,
              },
            },
            required: ['city'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'get_forecast') {
        const city = request.params.arguments.city;
        const days = Math.min(request.params.arguments.days || 3, 5);

        try {
          const response = await this.axiosInstance.get('forecast', {
            params: {
              q: city,
              cnt: days * 8,
            },
          });

          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  response.data.list.map(item => ({
                    date: item.dt_txt,
                    temperature: item.main.temp,
                    conditions: item.weather[0].description,
                    humidity: item.main.humidity,
                    wind_speed: item.wind.speed,
                  })),
                  null,
                  2
                ),
              },
            ],
          };
        } catch (error) {
          if (axios.isAxiosError(error)) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Weather API error: ${error.response?.data.message ?? error.message}`,
                },
              ],
              isError: true,
            };
          }
          throw error;
        }
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Weather MCP server running on stdio');
  }
}

const server = new WeatherServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Какая погода в Москве?"
ИИ: [использует MCP-сервер] "В Москве сейчас 22°C, солнечно, влажность 45%, ветер 3 м/с"

Пример 3: Сервер для работы с заметками

MCP-сервер для создания, чтения и управления заметками.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';

const NOTES_DIR = process.env.NOTES_DIR || './notes';

class NotesServer {
  constructor() {
    this.server = new Server(
      {
        name: 'notes-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureNotesDir() {
    try {
      await fs.mkdir(NOTES_DIR, { recursive: true });
    } catch (error) {
      console.error('Error creating notes directory:', error);
    }
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'create_note',
          description: 'Create a new note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note',
              },
              content: {
                type: 'string',
                description: 'Content of the note',
              },
            },
            required: ['title', 'content'],
          },
        },
        {
          name: 'list_notes',
          description: 'List all available notes',
          inputSchema: {
            type: 'object',
            properties: {},
            required: [],
          },
        },
        {
          name: 'read_note',
          description: 'Read a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note to read',
              },
            },
            required: ['title'],
          },
        },
        {
          name: 'delete_note',
          description: 'Delete a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note to delete',
              },
            },
            required: ['title'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      await this.ensureNotesDir();

      switch (request.params.name) {
        case 'create_note': {
          const { title, content } = request.params.arguments;
          const filename = this.titleToFilename(title);
          await fs.writeFile(path.join(NOTES_DIR, filename), content, 'utf8');
          return {
            content: [
              {
                type: 'text',
                text: `Note "${title}" created successfully.`,
              },
            ],
          };
        }
        case 'list_notes': {
          const files = await fs.readdir(NOTES_DIR);
          const notes = files
            .filter(file => file.endsWith('.txt'))
            .map(file => this.filenameToTitle(file));
          return {
            content: [
              {
                type: 'text',
                text: notes.length > 0 
                  ? `Available notes:\n${notes.map(note => `- ${note}`).join('\n')}`
                  : 'No notes found.',
              },
            ],
          };
        }
        case 'read_note': {
          const { title } = request.params.arguments;
          const filename = this.titleToFilename(title);
          try {
            const content = await fs.readFile(path.join(NOTES_DIR, filename), 'utf8');
            return {
              content: [
                {
                  type: 'text',
                  text: `# ${title}\n\n${content}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Note "${title}" not found.`,
                },
              ],
              isError: true,
            };
          }
        }
        case 'delete_note': {
          const { title } = request.params.arguments;
          const filename = this.titleToFilename(title);
          try {
            await fs.unlink(path.join(NOTES_DIR, filename));
            return {
              content: [
                {
                  type: 'text',
                  text: `Note "${title}" deleted successfully.`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error deleting note "${title}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  titleToFilename(title) {
    return `${title.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}.txt`;
  }

  filenameToTitle(filename) {
    return filename.replace(/\.txt$/, '').replace(/_/g, ' ');
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Notes MCP server running on stdio');
  }
}

const server = new NotesServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Создай заметку с заголовком 'Покупки' и содержанием 'Молоко, хлеб, яйца'"
ИИ: [использует MCP-сервер] "Заметка 'Покупки' успешно создана."

Пользователь: "Покажи мне список всех заметок"
ИИ: [использует MCP-сервер] "Доступные заметки:
- Покупки"

Пользователь: "Прочитай заметку 'Покупки'"
ИИ: [использует MCP-сервер] "# Покупки

Молоко, хлеб, яйца"

Пример 4: Поисковый сервер

MCP-сервер для поиска информации в интернете.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
import cheerio from 'cheerio';

class SearchServer {
  constructor() {
    this.server = new Server(
      {
        name: 'search-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'search_web',
          description: 'Search the web for information',
          inputSchema: {
            type: 'object',
            properties: {
              query: {
                type: 'string',
                description: 'Search query',
              },
              num_results: {
                type: 'number',
                description: 'Number of results to return',
                minimum: 1,
                maximum: 10,
              },
            },
            required: ['query'],
          },
        },
        {
          name: 'fetch_url',
          description: 'Fetch and summarize the content of a URL',
          inputSchema: {
            type: 'object',
            properties: {
              url: {
                type: 'string',
                description: 'URL to fetch',
              },
            },
            required: ['url'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'search_web': {
          const { query, num_results = 5 } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой поисковой API
            const response = await axios.get('https://api.example.com/search', {
              params: {
                q: query,
                limit: num_results,
              },
              headers: {
                'Authorization': `Bearer ${process.env.SEARCH_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Search results for "${query}":\n\n` +
                    response.data.results.map((result, index) => 
                      `${index + 1}. ${result.title}\n   ${result.url}\n   ${result.snippet}\n`
                    ).join('\n'),
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error searching for "${query}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'fetch_url': {
          const { url } = request.params.arguments;
          
          try {
            const response = await axios.get(url);
            const $ = cheerio.load(response.data);
            
            // Базовое извлечение текста
            const title = $('title').text().trim();
            const metaDescription = $('meta[name="description"]').attr('content') || '';
            
            // Извлечение основного содержимого (это базовая реализация)
            let content = '';
            $('p').each((i, elem) => {
              content += $(elem).text().trim() + '\n\n';
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `# ${title}\n\n${metaDescription}\n\n## Содержание страницы:\n\n${content}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error fetching URL "${url}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Search MCP server running on stdio');
  }
}

const server = new SearchServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Найди информацию о солнечной системе"
ИИ: [использует MCP-сервер] "Результаты поиска для 'солнечная система':
1. Солнечная система - Википедия
   https://ru.wikipedia.org/wiki/Солнечная_система
   Солнечная система — планетная система, включающая центральную звезду Солнце и все естественные космические объекты...

2. ..."

Пользователь: "Покажи информацию с сайта о солнечной системе"
ИИ: [использует MCP-сервер fetch_url со ссылкой из результатов поиска]

Пример 5: Сервер переводчик

MCP-сервер для перевода текста между разными языками.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';

const TRANSLATION_API_KEY = process.env.TRANSLATION_API_KEY;
if (!TRANSLATION_API_KEY) {
  throw new Error('TRANSLATION_API_KEY environment variable is required');
}

class TranslationServer {
  constructor() {
    this.server = new Server(
      {
        name: 'translation-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'translate_text',
          description: 'Translate text between languages',
          inputSchema: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'Text to translate',
              },
              source_language: {
                type: 'string',
                description: 'Source language code (e.g., "en", "ru", "fr")',
              },
              target_language: {
                type: 'string',
                description: 'Target language code (e.g., "en", "ru", "fr")',
              },
            },
            required: ['text', 'target_language'],
          },
        },
        {
          name: 'detect_language',
          description: 'Detect the language of a text',
          inputSchema: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'Text to analyze',
              },
            },
            required: ['text'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'translate_text': {
          const { text, source_language, target_language } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой API перевода
            const response = await axios.post('https://api.example.com/translate', {
              text,
              source: source_language || 'auto',
              target: target_language,
            }, {
              headers: {
                'Authorization': `Bearer ${TRANSLATION_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Перевод: ${response.data.translatedText}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error translating text: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'detect_language': {
          const { text } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой API
            const response = await axios.post('https://api.example.com/detect', {
              text,
            }, {
              headers: {
                'Authorization': `Bearer ${TRANSLATION_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Определенный язык: ${response.data.languageName} (${response.data.languageCode})\nУверенность: ${response.data.confidence * 100}%`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error detecting language: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Translation MCP server running on stdio');
  }
}

const server = new TranslationServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Переведи 'Здравствуйте, как дела?' на английский"
ИИ: [использует MCP-сервер] "Перевод: Hello, how are you?"

Пользователь: "Определи язык текста 'Bonjour, comment allez-vous?'"
ИИ: [использует MCP-сервер] "Определенный язык: Французский (fr)
Уверенность: 98%"

Пример 6: Сервер для работы с календарем

MCP-сервер для управления календарем и событиями.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';

const CALENDAR_FILE = process.env.CALENDAR_FILE || './calendar.json';

class CalendarServer {
  constructor() {
    this.server = new Server(
      {
        name: 'calendar-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureCalendarFile() {
    try {
      await fs.access(CALENDAR_FILE);
    } catch (error) {
      // Файл не существует, создаем его с пустым календарем
      await fs.writeFile(CALENDAR_FILE, JSON.stringify({ events: [] }, null, 2), 'utf8');
    }
  }

  async getCalendar() {
    await this.ensureCalendarFile();
    const data = await fs.readFile(CALENDAR_FILE, 'utf8');
    return JSON.parse(data);
  }

  async saveCalendar(calendar) {
    await fs.writeFile(CALENDAR_FILE, JSON.stringify(calendar, null, 2), 'utf8');
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'add_event',
          description: 'Add a new event to the calendar',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the event',
              },
              date: {
                type: 'string',
                description: 'Date of the event (YYYY-MM-DD)',
              },
              time: {
                type: 'string',
                description: 'Time of the event (HH:MM)',
              },
              description: {
                type: 'string',
                description: 'Description of the event',
              },
            },
            required: ['title', 'date'],
          },
        },
        {
          name: 'list_events',
          description: 'List events for a specific date or date range',
          inputSchema: {
            type: 'object',
            properties: {
              date: {
                type: 'string',
                description: 'Date to list events for (YYYY-MM-DD)',
              },
              start_date: {
                type: 'string',
                description: 'Start date for range (YYYY-MM-DD)',
              },
              end_date: {
                type: 'string',
                description: 'End date for range (YYYY-MM-DD)',
              },
            },
          },
        },
        {
          name: 'delete_event',
          description: 'Delete an event from the calendar',
          inputSchema: {
            type: 'object',
            properties: {
              event_id: {
                type: 'string',
                description: 'ID of the event to delete',
              },
            },
            required: ['event_id'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'add_event': {
          const { title, date, time, description } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          const newEvent = {
            id: Date.now().toString(),
            title,
            date,
            time: time || '',
            description: description || '',
            created_at: new Date().toISOString(),
          };
          
          calendar.events.push(newEvent);
          await this.saveCalendar(calendar);
          
          return {
            content: [
              {
                type: 'text',
                text: `Событие "${title}" добавлено в календарь на ${date}${time ? ' в ' + time : ''}.`,
              },
            ],
          };
        }
        
        case 'list_events': {
          const { date, start_date, end_date } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          let filteredEvents = [];
          
          if (date) {
            // Фильтрация по конкретной дате
            filteredEvents = calendar.events.filter(event => event.date === date);
          } else if (start_date && end_date) {
            // Фильтрация по диапазону дат
            filteredEvents = calendar.events.filter(event => {
              return event.date >= start_date && event.date <= end_date;
            });
          } else {
            // Возвращаем все события
            filteredEvents = calendar.events;
          }
          
          // Сортировка по дате и времени
          filteredEvents.sort((a, b) => {
            if (a.date !== b.date) return a.date.localeCompare(b.date);
            return a.time.localeCompare(b.time);
          });
          
          if (filteredEvents.length === 0) {
            return {
              content: [
                {
                  type: 'text',
                  text: 'Нет событий для отображения.',
                },
              ],
            };
          }
          
          const eventsText = filteredEvents.map(event => {
            return `ID: ${event.id}\nДата: ${event.date}${event.time ? ' ' + event.time : ''}\nНазвание: ${event.title}${event.description ? '\nОписание: ' + event.description : ''}`;
          }).join('\n\n');
          
          return {
            content: [
              {
                type: 'text',
                text: `События:\n\n${eventsText}`,
              },
            ],
          };
        }
        
        case 'delete_event': {
          const { event_id } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          const eventIndex = calendar.events.findIndex(event => event.id === event_id);
          
          if (eventIndex === -1) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Событие с ID ${event_id} не найдено.`,
                },
              ],
              isError: true,
            };
          }
          
          const deletedEvent = calendar.events[eventIndex];
          calendar.events.splice(eventIndex, 1);
          await this.saveCalendar(calendar);
          
          return {
            content: [
              {
                type: 'text',
                text: `Событие "${deletedEvent.title}" удалено из календаря.`,
              },
            ],
          };
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Calendar MCP server running on stdio');
  }
}

const server = new CalendarServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Добавь встречу с клиентом на 15 апреля в 14:00"
ИИ: [использует MCP-сервер] "Событие 'Встреча с клиентом' добавлено в календарь на 2025-04-15 в 14:00."

Пользователь: "Покажи мои события на апрель"
ИИ: [использует MCP-сервер] "События:

ID: 1710561723456
Дата: 2025-04-15 14:00
Название: Встреча с клиентом"

Пример 7: Сервер для работы с задачами (TODO-лист)

MCP-сервер для управления списком задач.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';

const TASKS_FILE = process.env.TASKS_FILE || './tasks.json';

class TasksServer {
  constructor() {
    this.server = new Server(
      {
        name: 'tasks-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureTasksFile() {
    try {
      await fs.access(TASKS_FILE);
    } catch (error) {
      // Файл не существует, создаем его с пустым списком задач
      await fs.writeFile(TASKS_FILE, JSON.stringify({ tasks: [] }, null, 2), 'utf8');
    }
  }

  async getTasks() {
    await this.ensureTasksFile();
    const data = await fs.readFile(TASKS_FILE, 'utf8');
    return JSON.parse(data);
  }

  async saveTasks(tasksData) {
    await fs.writeFile(TASKS_FILE, JSON.stringify(tasksData, null, 2), 'utf8');
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'add_task',
          description: 'Add a new task to the todo list',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the task',
              },
              priority: {
                type: 'string',
                description: 'Priority of the task (low, medium, high)',
                enum: ['low', 'medium', 'high'],
              },
              due_date: {
                type: 'string',
                description: 'Due date for the task (YYYY-MM-DD)',
              },
            },
            required: ['title'],
          },
        },
        {
          name: 'list_tasks',
          description: 'List all tasks or filter by status',
          inputSchema: {
            type: 'object',
            properties: {
              status: {
                type: 'string',
                description: 'Filter tasks by status (pending, completed)',
                enum: ['pending', 'completed'],
              },
              priority: {
                type: 'string',
                description: 'Filter tasks by priority (low, medium, high)',
                enum: ['low', 'medium', 'high'],
              },
            },
          },
        },
        {
          name: 'complete_task',
          description: 'Mark a task as completed',
          inputSchema: {
            type: 'object',
            properties: {
              task_id: {
                type: 'string',
                description: 'ID of the task to mark as completed',
              },
            },
            required: ['task_id'],
          },
        },
        {
          name: 'delete_task',
          description: 'Delete a task from the todo list',
          inputSchema: {
            type: 'object',
            properties: {
              task_id: {
                type: 'string',
                description: 'ID of the task to delete',
              },
            },
            required: ['task_id'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'add_task': {
          const { title, priority = 'medium', due_date } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          const newTask = {
            id: Date.now().toString(),
            title,
            priority,
            due_date: due_date || null,
            status: 'pending',
            created_at: new Date().toISOString(),
          };
          
          tasksData.tasks.push(newTask);
          await this.saveTasks(tasksData);
          
          return {
            content: [
              {
                type: 'text',
                text: `Задача "${title}" добавлена в список.`,
              },
            ],
          };
        }
        
        case 'list_tasks': {
          const { status, priority } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          let filteredTasks = tasksData.tasks;
          
          if (status) {
            filteredTasks = filteredTasks.filter(task => task.status === status);
          }
          
          if (priority) {
            filteredTasks = filteredTasks.filter(task => task.priority === priority);
          }
          
          // Сортировка по приоритету и дате создания
          const priorityOrder = { high: 0, medium: 1, low: 2 };
          filteredTasks.sort((a, b) => {
            if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
              return priorityOrder[a.priority] - priorityOrder[b.priority];
            }
            return new Date(a.created_at) - new Date(b.created_at);
          });
          
          if (filteredTasks.length === 0) {
            return {
              content: [
                {
                  type: 'text',
                  text: 'Нет задач для отображения.',
                },
              ],
            };
          }
          
          const tasksText = filteredTasks.map(task => {
            const statusEmoji = task.status === 'completed' ? '✅' : '⏳';
            const priorityEmoji = {
              low: '🟢',
              medium: '🟡',
              high: '🔴',
            }[task.priority];
            
            return `${statusEmoji} ${priorityEmoji} [${task.id}] ${task.title}${task.due_date ? ' (до ' + task.due_date + ')' : ''}`;
          }).join('\n');
          
          return {
            content: [
              {
                type: 'text',
                text: `Задачи:\n\n${tas
# Всеобъемлющее руководство по MCP-серверам

## Содержание

1. [Введение в MCP](#введение-в-mcp)
2. [Что такое MCP-серверы?](#что-такое-mcp-серверы)
3. [Основные концепции](#основные-концепции)
4. [Создание вашего первого MCP-сервера](#создание-вашего-первого-mcp-сервера)
5. [Примеры использования MCP-серверов](#примеры-использования-mcp-серверов)
6. [Продвинутые возможности](#продвинутые-возможности)
7. [Лучшие практики](#лучшие-практики)
8. [Устранение неполадок](#устранение-неполадок)
9. [Заключение](#заключение)

---

## Введение в MCP

MCP (Model Context Protocol)  это протокол, который позволяет крупным языковым моделям (LLM) и другим интеллектуальным системам взаимодействовать с внешними сервисами и ресурсами. Представьте это как мост, который соединяет мощные ИИ-системы (такие как Claude) с внешним миром  интернетом, базами данных, API и другими источниками информации.

### Почему это важно?

Представьте, что вы разговариваете с умным помощником, но он не может:
- Проверить актуальную погоду
- Найти что-то в интернете 
- Взаимодействовать с вашими файлами
- Использовать калькулятор
- Отправить email

Это существенно ограничивает его полезность. MCP решает эту проблему, позволяя ИИ-системам обращаться к внешним сервисам и расширять свои возможности.

### Простая аналогия

Представьте MCP как "USB-порт для ИИ". Так же как USB-порт позволяет подключать к компьютеру разные устройства (камеры, мышки, принтеры), MCP позволяет подключать к ИИ разные инструменты и сервисы.

---

## Что такое MCP-серверы?

MCP-сервер  это программа, которая:

1. **Предоставляет определенную функциональность** (например, доступ к погоде, работу с файлами и т.д.)
2. **Говорит на языке протокола MCP**, чтобы ИИ-системы могли ее использовать
3. **Работает как "мост"** между ИИ-системой и внешними сервисами/API

### Простая аналогия

Если представить, что ИИ  это клиент ресторана, то MCP-серверы  это официанты, которые приносят блюда (информацию/сервисы) с кухни (интернета/внешних API).

### Что могут делать MCP-серверы?

MCP-серверы могут предоставлять ИИ доступ к:
- Поиску в интернете
- Прогнозу погоды
- Новостям
- Базам данных
- Калькуляторам
- Системным операциям
- API сторонних сервисов (Twitter, Spotify, YouTube и т.д.)
- И многому другому!

---

## Основные концепции

Перед тем как мы перейдем к примерам, давайте разберемся с основными концепциями, которые нужно понимать:

### 1. Инструменты (Tools)

**Инструменты**  это функции или операции, которые MCP-сервер может выполнять. Например, "получить прогноз погоды", "найти информацию в Google", "отправить email".

Аналогия: Инструменты — это кнопки на пульте телевизора. Каждая кнопка выполняет определенное действие.


### 2. Ресурсы (Resources)

**Ресурсы** — это данные или информация, к которым MCP-сервер обеспечивает доступ. Например, текущая погода, информация о фильме, данные из базы.

Аналогия: Ресурсы — это книги в библиотеке. ИИ может "взять книгу" и прочитать информацию из нее.


### 3. Шаблоны ресурсов (Resource Templates)

**Шаблоны ресурсов** определяют, как можно обращаться к динамическим ресурсам. Например, шаблон `weather://{город}/current` позволяет запрашивать погоду для любого города.

Аналогия: Шаблон ресурса — это как карточный каталог в библиотеке, который помогает найти нужную книгу по описанию.


### 4. URI (Uniform Resource Identifier)

**URI** — это уникальный идентификатор ресурса, который используется для обращения к конкретному ресурсу. Например, `weather://Moscow/current`.

Аналогия: URI — это как адрес в городе. Он позволяет точно найти нужный "дом" (ресурс).


### 5. Запросы и ответы (Requests & Responses)

MCP работает по модели запрос-ответ:
- ИИ отправляет **запрос** на MCP-сервер (например, "какая погода в Москве?")
- MCP-сервер обрабатывает запрос и отправляет **ответ** (например, "в Москве 22°C, солнечно")

Аналогия: Это как разговор по телефону — вы задаете вопрос, собеседник отвечает.


---

## Создание вашего первого MCP-сервера

Давайте рассмотрим процесс создания простого MCP-сервера шаг за шагом. Не беспокойтесь, если вы не знаете, как программировать — мы рассмотрим общую концепцию, а потом перейдем к готовым примерам.

### Шаг 1: Установка необходимых инструментов

Для создания MCP-сервера вам понадобятся:
- Node.js (среда выполнения JavaScript)
- npm (менеджер пакетов)
- MCP SDK (набор инструментов для работы с MCP)

### Шаг 2: Инициализация проекта

```bash
# Создаем новую директорию
mkdir my-weather-server
cd my-weather-server

# Инициализируем проект
npm init -y

# Устанавливаем MCP SDK и другие необходимые пакеты
npm install @modelcontextprotocol/sdk axios

Шаг 3: Создание простого MCP-сервера

Создадим файл index.js с базовым кодом MCP-сервера:

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

// Создаем экземпляр сервера
const server = new Server(
  {
    name: 'hello-world-server',
    version: '0.1.0',
  },
  {
    capabilities: {
      resources: {},
      tools: {},
    },
  }
);

// Добавляем обработчик ошибок
server.onerror = (error) => console.error('[MCP Error]', error);

// Запускаем сервер
const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);
console.error('Hello World MCP server running on stdio');

Шаг 4: Сборка и запуск

# Компилируем код (если необходимо)
npm run build

# Запускаем сервер
node index.js

Шаг 5: Конфигурация MCP-сервера

Чтобы ИИ-система могла использовать ваш MCP-сервер, необходимо добавить его в конфигурационный файл MCP:

{
  "mcpServers": {
    "hello-world": {
      "command": "node",
      "args": ["/путь/к/вашему/серверу/index.js"],
      "env": {}
    }
  }
}

Вот и всё! Теперь у вас есть простой MCP-сервер. Конечно, он пока ничего не делает, но это основа, на которой мы будем строить наши примеры.


Примеры использования MCP-серверов

Теперь давайте рассмотрим конкретные примеры MCP-серверов — от самых простых до сложных и специализированных.

Пример 1: "Hello World" MCP-сервер

Самый простой MCP-сервер, который просто возвращает приветствие.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';

class HelloWorldServer {
  constructor() {
    this.server = new Server(
      {
        name: 'hello-world-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'say_hello',
          description: 'Returns a friendly greeting',
          inputSchema: {
            type: 'object',
            properties: {
              name: {
                type: 'string',
                description: 'Name of the person to greet',
              }
            },
            required: ['name'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'say_hello') {
        const name = request.params.arguments.name;
        return {
          content: [
            {
              type: 'text',
              text: `Привет, ${name}! Как дела?`,
            },
          ],
        };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Hello World MCP server running on stdio');
  }
}

const server = new HelloWorldServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Поздоровайся со мной"
ИИ: [использует MCP-сервер] "Привет, [имя]! Как дела?"

Пример 2: Сервер погоды

MCP-сервер, который предоставляет информацию о погоде.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ListToolsRequestSchema,
  McpError,
  ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';

const API_KEY = process.env.OPENWEATHER_API_KEY;
if (!API_KEY) {
  throw new Error('OPENWEATHER_API_KEY environment variable is required');
}

class WeatherServer {
  constructor() {
    this.server = new Server(
      {
        name: 'weather-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.axiosInstance = axios.create({
      baseURL: 'http://api.openweathermap.org/data/2.5',
      params: {
        appid: API_KEY,
        units: 'metric',
      },
    });

    this.setupResourceHandlers();
    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupResourceHandlers() {
    this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
      resourceTemplates: [
        {
          uriTemplate: 'weather://{city}/current',
          name: 'Current weather for a given city',
          mimeType: 'application/json',
          description: 'Real-time weather data for a specified city',
        },
      ],
    }));

    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const match = request.params.uri.match(/^weather:\/\/([^/]+)\/current$/);
      if (!match) {
        throw new McpError(
          ErrorCode.InvalidRequest,
          `Invalid URI format: ${request.params.uri}`
        );
      }
      const city = decodeURIComponent(match[1]);

      try {
        const response = await this.axiosInstance.get('weather', {
          params: { q: city },
        });

        return {
          contents: [
            {
              uri: request.params.uri,
              mimeType: 'application/json',
              text: JSON.stringify(
                {
                  temperature: response.data.main.temp,
                  conditions: response.data.weather[0].description,
                  humidity: response.data.main.humidity,
                  wind_speed: response.data.wind.speed,
                  timestamp: new Date().toISOString(),
                },
                null,
                2
              ),
            },
          ],
        };
      } catch (error) {
        if (axios.isAxiosError(error)) {
          throw new McpError(
            ErrorCode.InternalError,
            `Weather API error: ${error.response?.data.message ?? error.message}`
          );
        }
        throw error;
      }
    });
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'get_forecast',
          description: 'Get weather forecast for a city',
          inputSchema: {
            type: 'object',
            properties: {
              city: {
                type: 'string',
                description: 'City name',
              },
              days: {
                type: 'number',
                description: 'Number of days (1-5)',
                minimum: 1,
                maximum: 5,
              },
            },
            required: ['city'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'get_forecast') {
        const city = request.params.arguments.city;
        const days = Math.min(request.params.arguments.days || 3, 5);

        try {
          const response = await this.axiosInstance.get('forecast', {
            params: {
              q: city,
              cnt: days * 8,
            },
          });

          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  response.data.list.map(item => ({
                    date: item.dt_txt,
                    temperature: item.main.temp,
                    conditions: item.weather[0].description,
                    humidity: item.main.humidity,
                    wind_speed: item.wind.speed,
                  })),
                  null,
                  2
                ),
              },
            ],
          };
        } catch (error) {
          if (axios.isAxiosError(error)) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Weather API error: ${error.response?.data.message ?? error.message}`,
                },
              ],
              isError: true,
            };
          }
          throw error;
        }
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Weather MCP server running on stdio');
  }
}

const server = new WeatherServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Какая погода в Москве?"
ИИ: [использует MCP-сервер] "В Москве сейчас 22°C, солнечно, влажность 45%, ветер 3 м/с"

Пример 3: Сервер для работы с заметками

MCP-сервер для создания, чтения и управления заметками.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';

const NOTES_DIR = process.env.NOTES_DIR || './notes';

class NotesServer {
  constructor() {
    this.server = new Server(
      {
        name: 'notes-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureNotesDir() {
    try {
      await fs.mkdir(NOTES_DIR, { recursive: true });
    } catch (error) {
      console.error('Error creating notes directory:', error);
    }
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'create_note',
          description: 'Create a new note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note',
              },
              content: {
                type: 'string',
                description: 'Content of the note',
              },
            },
            required: ['title', 'content'],
          },
        },
        {
          name: 'list_notes',
          description: 'List all available notes',
          inputSchema: {
            type: 'object',
            properties: {},
            required: [],
          },
        },
        {
          name: 'read_note',
          description: 'Read a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note to read',
              },
            },
            required: ['title'],
          },
        },
        {
          name: 'delete_note',
          description: 'Delete a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note to delete',
              },
            },
            required: ['title'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      await this.ensureNotesDir();

      switch (request.params.name) {
        case 'create_note': {
          const { title, content } = request.params.arguments;
          const filename = this.titleToFilename(title);
          await fs.writeFile(path.join(NOTES_DIR, filename), content, 'utf8');
          return {
            content: [
              {
                type: 'text',
                text: `Note "${title}" created successfully.`,
              },
            ],
          };
        }
        case 'list_notes': {
          const files = await fs.readdir(NOTES_DIR);
          const notes = files
            .filter(file => file.endsWith('.txt'))
            .map(file => this.filenameToTitle(file));
          return {
            content: [
              {
                type: 'text',
                text: notes.length > 0 
                  ? `Available notes:\n${notes.map(note => `- ${note}`).join('\n')}`
                  : 'No notes found.',
              },
            ],
          };
        }
        case 'read_note': {
          const { title } = request.params.arguments;
          const filename = this.titleToFilename(title);
          try {
            const content = await fs.readFile(path.join(NOTES_DIR, filename), 'utf8');
            return {
              content: [
                {
                  type: 'text',
                  text: `# ${title}\n\n${content}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Note "${title}" not found.`,
                },
              ],
              isError: true,
            };
          }
        }
        case 'delete_note': {
          const { title } = request.params.arguments;
          const filename = this.titleToFilename(title);
          try {
            await fs.unlink(path.join(NOTES_DIR, filename));
            return {
              content: [
                {
                  type: 'text',
                  text: `Note "${title}" deleted successfully.`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error deleting note "${title}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  titleToFilename(title) {
    return `${title.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}.txt`;
  }

  filenameToTitle(filename) {
    return filename.replace(/\.txt$/, '').replace(/_/g, ' ');
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Notes MCP server running on stdio');
  }
}

const server = new NotesServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Создай заметку с заголовком 'Покупки' и содержанием 'Молоко, хлеб, яйца'"
ИИ: [использует MCP-сервер] "Заметка 'Покупки' успешно создана."

Пользователь: "Покажи мне список всех заметок"
ИИ: [использует MCP-сервер] "Доступные заметки:
- Покупки"

Пользователь: "Прочитай заметку 'Покупки'"
ИИ: [использует MCP-сервер] "# Покупки

Молоко, хлеб, яйца"

Пример 4: Поисковый сервер

MCP-сервер для поиска информации в интернете.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
import cheerio from 'cheerio';

class SearchServer {
  constructor() {
    this.server = new Server(
      {
        name: 'search-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'search_web',
          description: 'Search the web for information',
          inputSchema: {
            type: 'object',
            properties: {
              query: {
                type: 'string',
                description: 'Search query',
              },
              num_results: {
                type: 'number',
                description: 'Number of results to return',
                minimum: 1,
                maximum: 10,
              },
            },
            required: ['query'],
          },
        },
        {
          name: 'fetch_url',
          description: 'Fetch and summarize the content of a URL',
          inputSchema: {
            type: 'object',
            properties: {
              url: {
                type: 'string',
                description: 'URL to fetch',
              },
            },
            required: ['url'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'search_web': {
          const { query, num_results = 5 } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой поисковой API
            const response = await axios.get('https://api.example.com/search', {
              params: {
                q: query,
                limit: num_results,
              },
              headers: {
                'Authorization': `Bearer ${process.env.SEARCH_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Search results for "${query}":\n\n` +
                    response.data.results.map((result, index) => 
                      `${index + 1}. ${result.title}\n   ${result.url}\n   ${result.snippet}\n`
                    ).join('\n'),
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error searching for "${query}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'fetch_url': {
          const { url } = request.params.arguments;
          
          try {
            const response = await axios.get(url);
            const $ = cheerio.load(response.data);
            
            // Базовое извлечение текста
            const title = $('title').text().trim();
            const metaDescription = $('meta[name="description"]').attr('content') || '';
            
            // Извлечение основного содержимого (это базовая реализация)
            let content = '';
            $('p').each((i, elem) => {
              content += $(elem).text().trim() + '\n\n';
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `# ${title}\n\n${metaDescription}\n\n## Содержание страницы:\n\n${content}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error fetching URL "${url}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Search MCP server running on stdio');
  }
}

const server = new SearchServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Найди информацию о солнечной системе"
ИИ: [использует MCP-сервер] "Результаты поиска для 'солнечная система':
1. Солнечная система - Википедия
   https://ru.wikipedia.org/wiki/Солнечная_система
   Солнечная система — планетная система, включающая центральную звезду Солнце и все естественные космические объекты...

2. ..."

Пользователь: "Покажи информацию с сайта о солнечной системе"
ИИ: [использует MCP-сервер fetch_url со ссылкой из результатов поиска]

Пример 5: Сервер переводчик

MCP-сервер для перевода текста между разными языками.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';

const TRANSLATION_API_KEY = process.env.TRANSLATION_API_KEY;
if (!TRANSLATION_API_KEY) {
  throw new Error('TRANSLATION_API_KEY environment variable is required');
}

class TranslationServer {
  constructor() {
    this.server = new Server(
      {
        name: 'translation-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'translate_text',
          description: 'Translate text between languages',
          inputSchema: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'Text to translate',
              },
              source_language: {
                type: 'string',
                description: 'Source language code (e.g., "en", "ru", "fr")',
              },
              target_language: {
                type: 'string',
                description: 'Target language code (e.g., "en", "ru", "fr")',
              },
            },
            required: ['text', 'target_language'],
          },
        },
        {
          name: 'detect_language',
          description: 'Detect the language of a text',
          inputSchema: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'Text to analyze',
              },
            },
            required: ['text'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'translate_text': {
          const { text, source_language, target_language } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой API перевода
            const response = await axios.post('https://api.example.com/translate', {
              text,
              source: source_language || 'auto',
              target: target_language,
            }, {
              headers: {
                'Authorization': `Bearer ${TRANSLATION_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Перевод: ${response.data.translatedText}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error translating text: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'detect_language': {
          const { text } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой API
            const response = await axios.post('https://api.example.com/detect', {
              text,
            }, {
              headers: {
                'Authorization': `Bearer ${TRANSLATION_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Определенный язык: ${response.data.languageName} (${response.data.languageCode})\nУверенность: ${response.data.confidence * 100}%`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error detecting language: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Translation MCP server running on stdio');
  }
}

const server = new TranslationServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Переведи 'Здравствуйте, как дела?' на английский"
ИИ: [использует MCP-сервер] "Перевод: Hello, how are you?"

Пользователь: "Определи язык текста 'Bonjour, comment allez-vous?'"
ИИ: [использует MCP-сервер] "Определенный язык: Французский (fr)
Уверенность: 98%"

Пример 6: Сервер для работы с календарем

MCP-сервер для управления календарем и событиями.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';

const CALENDAR_FILE = process.env.CALENDAR_FILE || './calendar.json';

class CalendarServer {
  constructor() {
    this.server = new Server(
      {
        name: 'calendar-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureCalendarFile() {
    try {
      await fs.access(CALENDAR_FILE);
    } catch (error) {
      // Файл не существует, создаем его с пустым календарем
      await fs.writeFile(CALENDAR_FILE, JSON.stringify({ events: [] }, null, 2), 'utf8');
    }
  }

  async getCalendar() {
    await this.ensureCalendarFile();
    const data = await fs.readFile(CALENDAR_FILE, 'utf8');
    return JSON.parse(data);
  }

  async saveCalendar(calendar) {
    await fs.writeFile(CALENDAR_FILE, JSON.stringify(calendar, null, 2), 'utf8');
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'add_event',
          description: 'Add a new event to the calendar',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the event',
              },
              date: {
                type: 'string',
                description: 'Date of the event (YYYY-MM-DD)',
              },
              time: {
                type: 'string',
                description: 'Time of the event (HH:MM)',
              },
              description: {
                type: 'string',
                description: 'Description of the event',
              },
            },
            required: ['title', 'date'],
          },
        },
        {
          name: 'list_events',
          description: 'List events for a specific date or date range',
          inputSchema: {
            type: 'object',
            properties: {
              date: {
                type: 'string',
                description: 'Date to list events for (YYYY-MM-DD)',
              },
              start_date: {
                type: 'string',
                description: 'Start date for range (YYYY-MM-DD)',
              },
              end_date: {
                type: 'string',
                description: 'End date for range (YYYY-MM-DD)',
              },
            },
          },
        },
        {
          name: 'delete_event',
          description: 'Delete an event from the calendar',
          inputSchema: {
            type: 'object',
            properties: {
              event_id: {
                type: 'string',
                description: 'ID of the event to delete',
              },
            },
            required: ['event_id'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'add_event': {
          const { title, date, time, description } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          const newEvent = {
            id: Date.now().toString(),
            title,
            date,
            time: time || '',
            description: description || '',
            created_at: new Date().toISOString(),
          };
          
          calendar.events.push(newEvent);
          await this.saveCalendar(calendar);
          
          return {
            content: [
              {
                type: 'text',
                text: `Событие "${title}" добавлено в календарь на ${date}${time ? ' в ' + time : ''}.`,
              },
            ],
          };
        }
        
        case 'list_events': {
          const { date, start_date, end_date } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          let filteredEvents = [];
          
          if (date) {
            // Фильтрация по конкретной дате
            filteredEvents = calendar.events.filter(event => event.date === date);
          } else if (start_date && end_date) {
            // Фильтрация по диапазону дат
            filteredEvents = calendar.events.filter(event => {
              return event.date >= start_date && event.date <= end_date;
            });
          } else {
            // Возвращаем все события
            filteredEvents = calendar.events;
          }
          
          // Сортировка по дате и времени
          filteredEvents.sort((a, b) => {
            if (a.date !== b.date) return a.date.localeCompare(b.date);
            return a.time.localeCompare(b.time);
          });
          
          if (filteredEvents.length === 0) {
            return {
              content: [
                {
                  type: 'text',
                  text: 'Нет событий для отображения.',
                },
              ],
            };
          }
          
          const eventsText = filteredEvents.map(event => {
            return `ID: ${event.id}\nДата: ${event.date}${event.time ? ' ' + event.time : ''}\nНазвание: ${event.title}${event.description ? '\nОписание: ' + event.description : ''}`;
          }).join('\n\n');
          
          return {
            content: [
              {
                type: 'text',
                text: `События:\n\n${eventsText}`,
              },
            ],
          };
        }
        
        case 'delete_event': {
          const { event_id } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          const eventIndex = calendar.events.findIndex(event => event.id === event_id);
          
          if (eventIndex === -1) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Событие с ID ${event_id} не найдено.`,
                },
              ],
              isError: true,
            };
          }
          
          const deletedEvent = calendar.events[eventIndex];
          calendar.events.splice(eventIndex, 1);
          await this.saveCalendar(calendar);
          
          return {
            content: [
              {
                type: 'text',
                text: `Событие "${deletedEvent.title}" удалено из календаря.`,
              },
            ],
          };
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Calendar MCP server running on stdio');
  }
}

const server = new CalendarServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Добавь встречу с клиентом на 15 апреля в 14:00"
ИИ: [использует MCP-сервер] "Событие 'Встреча с клиентом' добавлено в календарь на 2025-04-15 в 14:00."

Пользователь: "Покажи мои события на апрель"
ИИ: [использует MCP-сервер] "События:

ID: 1710561723456
Дата: 2025-04-15 14:00
Название: Встреча с клиентом"

Пример 7: Сервер для работы с задачами (TODO-лист)

MCP-сервер для управления списком задач.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';

const TASKS_FILE = process.env.TASKS_FILE || './tasks.json';

class TasksServer {
  constructor() {
    this.server = new Server(
      {
        name: 'tasks-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureTasksFile() {
    try {
      await fs.access(TASKS_FILE);
    } catch (error) {
      // Файл не существует, создаем его с пустым списком задач
      await fs.writeFile(TASKS_FILE, JSON.stringify({ tasks: [] }, null, 2), 'utf8');
    }
  }

  async getTasks() {
    await this.ensureTasksFile();
    const data = await fs.readFile(TASKS_FILE, 'utf8');
    return JSON.parse(data);
  }

  async saveTasks(tasksData) {
    await fs.writeFile(TASKS_FILE, JSON.stringify(tasksData, null, 2), 'utf8');
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'add_task',
          description: 'Add a new task to the todo list',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the task',
              },
              priority: {
                type: 'string',
                description: 'Priority of the task (low, medium, high)',
                enum: ['low', 'medium', 'high'],
              },
              due_date: {
                type: 'string',
                description: 'Due date for the task (YYYY-MM-DD)',
              },
            },
            required: ['title'],
          },
        },
        {
          name: 'list_tasks',
          description: 'List all tasks or filter by status',
          inputSchema: {
            type: 'object',
            properties: {
              status: {
                type: 'string',
                description: 'Filter tasks by status (pending, completed)',
                enum: ['pending', 'completed'],
              },
              priority: {
                type: 'string',
                description: 'Filter tasks by priority (low, medium, high)',
                enum: ['low', 'medium', 'high'],
              },
            },
          },
        },
        {
          name: 'complete_task',
          description: 'Mark a task as completed',
          inputSchema: {
            type: 'object',
            properties: {
              task_id: {
                type: 'string',
                description: 'ID of the task to mark as completed',
              },
            },
            required: ['task_id'],
          },
        },
        {
          name: 'delete_task',
          description: 'Delete a task from the todo list',
          inputSchema: {
            type: 'object',
            properties: {
              task_id: {
                type: 'string',
                description: 'ID of the task to delete',
              },
            },
            required: ['task_id'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'add_task': {
          const { title, priority = 'medium', due_date } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          const newTask = {
            id: Date.now().toString(),
            title,
            priority,
            due_date: due_date || null,
            status: 'pending',
            created_at: new Date().toISOString(),
          };
          
          tasksData.tasks.push(newTask);
          await this.saveTasks(tasksData);
          
          return {
            content: [
              {
                type: 'text',
                text: `Задача "${title}" добавлена в список.`,
              },
            ],
          };
        }
        
        case 'list_tasks': {
          const { status, priority } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          let filteredTasks = tasksData.tasks;
          
          if (status) {
            filteredTasks = filteredTasks.filter(task => task.status === status);
          }
          
          if (priority) {
            filteredTasks = filteredTasks.filter(task => task.priority === priority);
          }
          
          // Сортировка по приоритету и дате создания
          const priorityOrder = { high: 0, medium: 1, low: 2 };
          filteredTasks.sort((a, b) => {
            if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
              return priorityOrder[a.priority] - priorityOrder[b.priority];
            }
            return new Date(a.created_at) - new Date(b.created_at);
          });
          
          if (filteredTasks.length === 0) {
            return {
              content: [
                {
                  type: 'text',
                  text: 'Нет задач для отображения.',
                },
              ],
            };
          }
          
          const tasksText = filteredTasks.map(task => {
            const statusEmoji = task.status === 'completed' ? '✅' : '⏳';
            const priorityEmoji = {
              low: '🟢',
              medium: '🟡',
              high: '🔴',
            }[task.priority];
            
            return `${statusEmoji} ${priorityEmoji} [${task.id}] ${task.title}${task.due_date ? ' (до ' + task.due_date + ')' : ''}`;
          }).join('\n');
          
          return {
            content: [
              {
                type: 'text',
                text: `Задачи:\n\n${tasksText}`,
              },
            ],
          };
        }
        
        case 'complete_task': {
          const { task_id } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          const taskIndex = tasksData.tasks.findIndex(task => task.id === task_id);
          
          if (taskIndex === -1) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Задача с ID ${task_id} не найдена.`,
                },
              ],
              isError: true,
            };
          }
          
          tasksData.tasks[taskIndex].status = 'completed';
          await this.saveTasks(tasksData);
          
          return {
            content: [
              {
                type: 'text',
                text: `Задача "${tasksData.tasks[taskIndex].title}" отмечена как выполненная.`,
              },
            ],
          };
        }
        
        case 'delete_task': {
          const { task_id } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          const taskIndex = tasksData.tasks.findIndex(task => task.id === task_id);
          
          if (taskIndex === -1) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Задача с ID ${task_id} не найдена.`,
                },
              ],
              isError: true,
            };
          }
          
          const deletedTask = tasksData.tasks[taskIndex];
          tasksData.tasks.splice(taskIndex, 1);
          await this.saveTasks(tasksData);
          
          return {
            content: [
              {
                type: 'text',
                text: `Задача "${deletedTask.title}" удалена из списка.`,
              },
            ],
          };
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Tasks MCP server running on stdio');
  }
}

const server = new TasksServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Добавь задачу 'Купить продукты' с высоким приоритетом"
ИИ: [использует MCP-сервер] "Задача 'Купить продукты' добавлена в список."

Пользователь: "Покажи мои задачи"
ИИ: [использует MCP-сервер] "Задачи:

🔴 ⏳ [1710561823456] Купить продукты"

Пользователь: "Отметь задачу с ID 1710561823456 как выполненную"
ИИ: [использует MCP-сервер] "Задача 'Купить продукты' отмечена как выполненная."

Пример 8: Сервер для работы с базой данных

MCP-сервер для выполнения запросов к базе данных.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';

const DB_PATH = process.env.DB_PATH || './database.sqlite';

class DatabaseServer {
  constructor() {
    this.server = new Server(
      {
        name: 'database-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async getDbConnection() {
    if (!this.db) {
      this.db = await open({
        filename: DB_PATH,
        driver: sqlite3.Database,
      });
    }
    return this.db;
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'execute_query',
          description: 'Execute an SQL query on the database',
          inputSchema: {
            type: 'object',
            properties: {
              query: {
                type: 'string',
                description: 'SQL query to execute',
              },
              params: {
                type: 'array',
                description: 'Parameters for the query',
                items: {
                  type: ['string', 'number', 'boolean', 'null'],
                },
              },
            },
            required: ['query'],
          },
        },
        {
          name: 'get_tables',
          description: 'Get a list of tables in the database',
          inputSchema: {
            type: 'object',
            properties: {},
            required: [],
          },
        },
        {
          name: 'get_table_schema',
          description: 'Get the schema for a specific table',
          inputSchema: {
            type: 'object',
            properties: {
              table_name: {
                type: 'string',
                description: 'Name of the table',
              },
            },
            required: ['table_name'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const db = await this.getDbConnection();

      switch (request.params.name) {
        case 'execute_query': {
          const { query, params = [] } = request.params.arguments;
          
          try {
            // Проверка, является ли запрос SELECT или нет
            const isSelect = query.trim().toUpperCase().startsWith('SELECT');
            
            let result;
            if (isSelect) {
              result = await db.all(query, params);
            } else {
              result = await db.run(query, params);
            }
            
            return {
              content: [
                {
                  type: 'text',
                  text: isSelect 
                    ? `Результаты запроса:\n\n${JSON.stringify(result, null, 2)}`
                    : `Запрос выполнен успешно. Затронуто строк: ${result.changes || 0}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Ошибка выполнения запроса: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'get_tables': {
          try {
            const result = await db.all("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'");
            const tables = result.map(row => row.name);
            
            return {
              content: [
                {
                  type: 'text',
                  text: tables.length > 0
                    ? `Таблицы в базе данных:\n\n${tables.join('\n')}`
                    : 'В базе данных нет таблиц.',
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Ошибка получения списка таблиц: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'get_table_schema': {
          const { table_name } = request.params.arguments;
          
          try {
            const result = await db.all(`PRAGMA table_info(${table_name})`);
            
            if (result.length === 0) {
              return {
                content: [
                  {
                    type: 'text',
                    text: `Таблица '${table_name}' не найдена.`,
                  },
                ],
                isError: true,
              };
            }
            
            const columns = result.map(col => {
              return `${col.
# Всеобъемлющее руководство по MCP-серверам

## Содержание

1. [Введение в MCP](#введение-в-mcp)
2. [Что такое MCP-серверы?](#что-такое-mcp-серверы)
3. [Основные концепции](#основные-концепции)
4. [Создание вашего первого MCP-сервера](#создание-вашего-первого-mcp-сервера)
5. [Примеры использования MCP-серверов](#примеры-использования-mcp-серверов)
6. [Продвинутые возможности](#продвинутые-возможности)
7. [Лучшие практики](#лучшие-практики)
8. [Устранение неполадок](#устранение-неполадок)
9. [Заключение](#заключение)

---

## Введение в MCP

MCP (Model Context Protocol)  это протокол, который позволяет крупным языковым моделям (LLM) и другим интеллектуальным системам взаимодействовать с внешними сервисами и ресурсами. Представьте это как мост, который соединяет мощные ИИ-системы (такие как Claude) с внешним миром  интернетом, базами данных, API и другими источниками информации.

### Почему это важно?

Представьте, что вы разговариваете с умным помощником, но он не может:
- Проверить актуальную погоду
- Найти что-то в интернете 
- Взаимодействовать с вашими файлами
- Использовать калькулятор
- Отправить email

Это существенно ограничивает его полезность. MCP решает эту проблему, позволяя ИИ-системам обращаться к внешним сервисам и расширять свои возможности.

### Простая аналогия

Представьте MCP как "USB-порт для ИИ". Так же как USB-порт позволяет подключать к компьютеру разные устройства (камеры, мышки, принтеры), MCP позволяет подключать к ИИ разные инструменты и сервисы.

---

## Что такое MCP-серверы?

MCP-сервер  это программа, которая:

1. **Предоставляет определенную функциональность** (например, доступ к погоде, работу с файлами и т.д.)
2. **Говорит на языке протокола MCP**, чтобы ИИ-системы могли ее использовать
3. **Работает как "мост"** между ИИ-системой и внешними сервисами/API

### Простая аналогия

Если представить, что ИИ  это клиент ресторана, то MCP-серверы  это официанты, которые приносят блюда (информацию/сервисы) с кухни (интернета/внешних API).

### Что могут делать MCP-серверы?

MCP-серверы могут предоставлять ИИ доступ к:
- Поиску в интернете
- Прогнозу погоды
- Новостям
- Базам данных
- Калькуляторам
- Системным операциям
- API сторонних сервисов (Twitter, Spotify, YouTube и т.д.)
- И многому другому!

---

## Основные концепции

Перед тем как мы перейдем к примерам, давайте разберемся с основными концепциями, которые нужно понимать:

### 1. Инструменты (Tools)

**Инструменты**  это функции или операции, которые MCP-сервер может выполнять. Например, "получить прогноз погоды", "найти информацию в Google", "отправить email".

Аналогия: Инструменты — это кнопки на пульте телевизора. Каждая кнопка выполняет определенное действие.


### 2. Ресурсы (Resources)

**Ресурсы** — это данные или информация, к которым MCP-сервер обеспечивает доступ. Например, текущая погода, информация о фильме, данные из базы.

Аналогия: Ресурсы — это книги в библиотеке. ИИ может "взять книгу" и прочитать информацию из нее.


### 3. Шаблоны ресурсов (Resource Templates)

**Шаблоны ресурсов** определяют, как можно обращаться к динамическим ресурсам. Например, шаблон `weather://{город}/current` позволяет запрашивать погоду для любого города.

Аналогия: Шаблон ресурса — это как карточный каталог в библиотеке, который помогает найти нужную книгу по описанию.


### 4. URI (Uniform Resource Identifier)

**URI** — это уникальный идентификатор ресурса, который используется для обращения к конкретному ресурсу. Например, `weather://Moscow/current`.

Аналогия: URI — это как адрес в городе. Он позволяет точно найти нужный "дом" (ресурс).


### 5. Запросы и ответы (Requests & Responses)

MCP работает по модели запрос-ответ:
- ИИ отправляет **запрос** на MCP-сервер (например, "какая погода в Москве?")
- MCP-сервер обрабатывает запрос и отправляет **ответ** (например, "в Москве 22°C, солнечно")

Аналогия: Это как разговор по телефону — вы задаете вопрос, собеседник отвечает.


---

## Создание вашего первого MCP-сервера

Давайте рассмотрим процесс создания простого MCP-сервера шаг за шагом. Не беспокойтесь, если вы не знаете, как программировать — мы рассмотрим общую концепцию, а потом перейдем к готовым примерам.

### Шаг 1: Установка необходимых инструментов

Для создания MCP-сервера вам понадобятся:
- Node.js (среда выполнения JavaScript)
- npm (менеджер пакетов)
- MCP SDK (набор инструментов для работы с MCP)

### Шаг 2: Инициализация проекта

```bash
# Создаем новую директорию
mkdir my-weather-server
cd my-weather-server

# Инициализируем проект
npm init -y

# Устанавливаем MCP SDK и другие необходимые пакеты
npm install @modelcontextprotocol/sdk axios

Шаг 3: Создание простого MCP-сервера

Создадим файл index.js с базовым кодом MCP-сервера:

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

// Создаем экземпляр сервера
const server = new Server(
  {
    name: 'hello-world-server',
    version: '0.1.0',
  },
  {
    capabilities: {
      resources: {},
      tools: {},
    },
  }
);

// Добавляем обработчик ошибок
server.onerror = (error) => console.error('[MCP Error]', error);

// Запускаем сервер
const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);
console.error('Hello World MCP server running on stdio');

Шаг 4: Сборка и запуск

# Компилируем код (если необходимо)
npm run build

# Запускаем сервер
node index.js

Шаг 5: Конфигурация MCP-сервера

Чтобы ИИ-система могла использовать ваш MCP-сервер, необходимо добавить его в конфигурационный файл MCP:

{
  "mcpServers": {
    "hello-world": {
      "command": "node",
      "args": ["/путь/к/вашему/серверу/index.js"],
      "env": {}
    }
  }
}

Вот и всё! Теперь у вас есть простой MCP-сервер. Конечно, он пока ничего не делает, но это основа, на которой мы будем строить наши примеры.


Примеры использования MCP-серверов

Теперь давайте рассмотрим конкретные примеры MCP-серверов — от самых простых до сложных и специализированных.

Пример 1: "Hello World" MCP-сервер

Самый простой MCP-сервер, который просто возвращает приветствие.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';

class HelloWorldServer {
  constructor() {
    this.server = new Server(
      {
        name: 'hello-world-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'say_hello',
          description: 'Returns a friendly greeting',
          inputSchema: {
            type: 'object',
            properties: {
              name: {
                type: 'string',
                description: 'Name of the person to greet',
              }
            },
            required: ['name'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'say_hello') {
        const name = request.params.arguments.name;
        return {
          content: [
            {
              type: 'text',
              text: `Привет, ${name}! Как дела?`,
            },
          ],
        };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Hello World MCP server running on stdio');
  }
}

const server = new HelloWorldServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Поздоровайся со мной"
ИИ: [использует MCP-сервер] "Привет, [имя]! Как дела?"

Пример 2: Сервер погоды

MCP-сервер, который предоставляет информацию о погоде.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ListToolsRequestSchema,
  McpError,
  ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';

const API_KEY = process.env.OPENWEATHER_API_KEY;
if (!API_KEY) {
  throw new Error('OPENWEATHER_API_KEY environment variable is required');
}

class WeatherServer {
  constructor() {
    this.server = new Server(
      {
        name: 'weather-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.axiosInstance = axios.create({
      baseURL: 'http://api.openweathermap.org/data/2.5',
      params: {
        appid: API_KEY,
        units: 'metric',
      },
    });

    this.setupResourceHandlers();
    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupResourceHandlers() {
    this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
      resourceTemplates: [
        {
          uriTemplate: 'weather://{city}/current',
          name: 'Current weather for a given city',
          mimeType: 'application/json',
          description: 'Real-time weather data for a specified city',
        },
      ],
    }));

    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const match = request.params.uri.match(/^weather:\/\/([^/]+)\/current$/);
      if (!match) {
        throw new McpError(
          ErrorCode.InvalidRequest,
          `Invalid URI format: ${request.params.uri}`
        );
      }
      const city = decodeURIComponent(match[1]);

      try {
        const response = await this.axiosInstance.get('weather', {
          params: { q: city },
        });

        return {
          contents: [
            {
              uri: request.params.uri,
              mimeType: 'application/json',
              text: JSON.stringify(
                {
                  temperature: response.data.main.temp,
                  conditions: response.data.weather[0].description,
                  humidity: response.data.main.humidity,
                  wind_speed: response.data.wind.speed,
                  timestamp: new Date().toISOString(),
                },
                null,
                2
              ),
            },
          ],
        };
      } catch (error) {
        if (axios.isAxiosError(error)) {
          throw new McpError(
            ErrorCode.InternalError,
            `Weather API error: ${error.response?.data.message ?? error.message}`
          );
        }
        throw error;
      }
    });
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'get_forecast',
          description: 'Get weather forecast for a city',
          inputSchema: {
            type: 'object',
            properties: {
              city: {
                type: 'string',
                description: 'City name',
              },
              days: {
                type: 'number',
                description: 'Number of days (1-5)',
                minimum: 1,
                maximum: 5,
              },
            },
            required: ['city'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'get_forecast') {
        const city = request.params.arguments.city;
        const days = Math.min(request.params.arguments.days || 3, 5);

        try {
          const response = await this.axiosInstance.get('forecast', {
            params: {
              q: city,
              cnt: days * 8,
            },
          });

          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  response.data.list.map(item => ({
                    date: item.dt_txt,
                    temperature: item.main.temp,
                    conditions: item.weather[0].description,
                    humidity: item.main.humidity,
                    wind_speed: item.wind.speed,
                  })),
                  null,
                  2
                ),
              },
            ],
          };
        } catch (error) {
          if (axios.isAxiosError(error)) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Weather API error: ${error.response?.data.message ?? error.message}`,
                },
              ],
              isError: true,
            };
          }
          throw error;
        }
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Weather MCP server running on stdio');
  }
}

const server = new WeatherServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Какая погода в Москве?"
ИИ: [использует MCP-сервер] "В Москве сейчас 22°C, солнечно, влажность 45%, ветер 3 м/с"

Пример 3: Сервер для работы с заметками

MCP-сервер для создания, чтения и управления заметками.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';

const NOTES_DIR = process.env.NOTES_DIR || './notes';

class NotesServer {
  constructor() {
    this.server = new Server(
      {
        name: 'notes-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureNotesDir() {
    try {
      await fs.mkdir(NOTES_DIR, { recursive: true });
    } catch (error) {
      console.error('Error creating notes directory:', error);
    }
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'create_note',
          description: 'Create a new note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note',
              },
              content: {
                type: 'string',
                description: 'Content of the note',
              },
            },
            required: ['title', 'content'],
          },
        },
        {
          name: 'list_notes',
          description: 'List all available notes',
          inputSchema: {
            type: 'object',
            properties: {},
            required: [],
          },
        },
        {
          name: 'read_note',
          description: 'Read a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note to read',
              },
            },
            required: ['title'],
          },
        },
        {
          name: 'delete_note',
          description: 'Delete a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note to delete',
              },
            },
            required: ['title'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      await this.ensureNotesDir();

      switch (request.params.name) {
        case 'create_note': {
          const { title, content } = request.params.arguments;
          const filename = this.titleToFilename(title);
          await fs.writeFile(path.join(NOTES_DIR, filename), content, 'utf8');
          return {
            content: [
              {
                type: 'text',
                text: `Note "${title}" created successfully.`,
              },
            ],
          };
        }
        case 'list_notes': {
          const files = await fs.readdir(NOTES_DIR);
          const notes = files
            .filter(file => file.endsWith('.txt'))
            .map(file => this.filenameToTitle(file));
          return {
            content: [
              {
                type: 'text',
                text: notes.length > 0 
                  ? `Available notes:\n${notes.map(note => `- ${note}`).join('\n')}`
                  : 'No notes found.',
              },
            ],
          };
        }
        case 'read_note': {
          const { title } = request.params.arguments;
          const filename = this.titleToFilename(title);
          try {
            const content = await fs.readFile(path.join(NOTES_DIR, filename), 'utf8');
            return {
              content: [
                {
                  type: 'text',
                  text: `# ${title}\n\n${content}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Note "${title}" not found.`,
                },
              ],
              isError: true,
            };
          }
        }
        case 'delete_note': {
          const { title } = request.params.arguments;
          const filename = this.titleToFilename(title);
          try {
            await fs.unlink(path.join(NOTES_DIR, filename));
            return {
              content: [
                {
                  type: 'text',
                  text: `Note "${title}" deleted successfully.`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error deleting note "${title}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  titleToFilename(title) {
    return `${title.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}.txt`;
  }

  filenameToTitle(filename) {
    return filename.replace(/\.txt$/, '').replace(/_/g, ' ');
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Notes MCP server running on stdio');
  }
}

const server = new NotesServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Создай заметку с заголовком 'Покупки' и содержанием 'Молоко, хлеб, яйца'"
ИИ: [использует MCP-сервер] "Заметка 'Покупки' успешно создана."

Пользователь: "Покажи мне список всех заметок"
ИИ: [использует MCP-сервер] "Доступные заметки:
- Покупки"

Пользователь: "Прочитай заметку 'Покупки'"
ИИ: [использует MCP-сервер] "# Покупки

Молоко, хлеб, яйца"

Пример 4: Поисковый сервер

MCP-сервер для поиска информации в интернете.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
import cheerio from 'cheerio';

class SearchServer {
  constructor() {
    this.server = new Server(
      {
        name: 'search-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'search_web',
          description: 'Search the web for information',
          inputSchema: {
            type: 'object',
            properties: {
              query: {
                type: 'string',
                description: 'Search query',
              },
              num_results: {
                type: 'number',
                description: 'Number of results to return',
                minimum: 1,
                maximum: 10,
              },
            },
            required: ['query'],
          },
        },
        {
          name: 'fetch_url',
          description: 'Fetch and summarize the content of a URL',
          inputSchema: {
            type: 'object',
            properties: {
              url: {
                type: 'string',
                description: 'URL to fetch',
              },
            },
            required: ['url'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'search_web': {
          const { query, num_results = 5 } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой поисковой API
            const response = await axios.get('https://api.example.com/search', {
              params: {
                q: query,
                limit: num_results,
              },
              headers: {
                'Authorization': `Bearer ${process.env.SEARCH_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Search results for "${query}":\n\n` +
                    response.data.results.map((result, index) => 
                      `${index + 1}. ${result.title}\n   ${result.url}\n   ${result.snippet}\n`
                    ).join('\n'),
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error searching for "${query}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'fetch_url': {
          const { url } = request.params.arguments;
          
          try {
            const response = await axios.get(url);
            const $ = cheerio.load(response.data);
            
            // Базовое извлечение текста
            const title = $('title').text().trim();
            const metaDescription = $('meta[name="description"]').attr('content') || '';
            
            // Извлечение основного содержимого (это базовая реализация)
            let content = '';
            $('p').each((i, elem) => {
              content += $(elem).text().trim() + '\n\n';
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `# ${title}\n\n${metaDescription}\n\n## Содержание страницы:\n\n${content}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error fetching URL "${url}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Search MCP server running on stdio');
  }
}

const server = new SearchServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Найди информацию о солнечной системе"
ИИ: [использует MCP-сервер] "Результаты поиска для 'солнечная система':
1. Солнечная система - Википедия
   https://ru.wikipedia.org/wiki/Солнечная_система
   Солнечная система — планетная система, включающая центральную звезду Солнце и все естественные космические объекты...

2. ..."

Пользователь: "Покажи информацию с сайта о солнечной системе"
ИИ: [использует MCP-сервер fetch_url со ссылкой из результатов поиска]

Пример 5: Сервер переводчик

MCP-сервер для перевода текста между разными языками.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';

const TRANSLATION_API_KEY = process.env.TRANSLATION_API_KEY;
if (!TRANSLATION_API_KEY) {
  throw new Error('TRANSLATION_API_KEY environment variable is required');
}

class TranslationServer {
  constructor() {
    this.server = new Server(
      {
        name: 'translation-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'translate_text',
          description: 'Translate text between languages',
          inputSchema: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'Text to translate',
              },
              source_language: {
                type: 'string',
                description: 'Source language code (e.g., "en", "ru", "fr")',
              },
              target_language: {
                type: 'string',
                description: 'Target language code (e.g., "en", "ru", "fr")',
              },
            },
            required: ['text', 'target_language'],
          },
        },
        {
          name: 'detect_language',
          description: 'Detect the language of a text',
          inputSchema: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'Text to analyze',
              },
            },
            required: ['text'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'translate_text': {
          const { text, source_language, target_language } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой API перевода
            const response = await axios.post('https://api.example.com/translate', {
              text,
              source: source_language || 'auto',
              target: target_language,
            }, {
              headers: {
                'Authorization': `Bearer ${TRANSLATION_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Перевод: ${response.data.translatedText}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error translating text: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'detect_language': {
          const { text } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой API
            const response = await axios.post('https://api.example.com/detect', {
              text,
            }, {
              headers: {
                'Authorization': `Bearer ${TRANSLATION_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Определенный язык: ${response.data.languageName} (${response.data.languageCode})\nУверенность: ${response.data.confidence * 100}%`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error detecting language: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Translation MCP server running on stdio');
  }
}

const server = new TranslationServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Переведи 'Здравствуйте, как дела?' на английский"
ИИ: [использует MCP-сервер] "Перевод: Hello, how are you?"

Пользователь: "Определи язык текста 'Bonjour, comment allez-vous?'"
ИИ: [использует MCP-сервер] "Определенный язык: Французский (fr)
Уверенность: 98%"

Пример 6: Сервер для работы с календарем

MCP-сервер для управления календарем и событиями.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';

const CALENDAR_FILE = process.env.CALENDAR_FILE || './calendar.json';

class CalendarServer {
  constructor() {
    this.server = new Server(
      {
        name: 'calendar-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureCalendarFile() {
    try {
      await fs.access(CALENDAR_FILE);
    } catch (error) {
      // Файл не существует, создаем его с пустым календарем
      await fs.writeFile(CALENDAR_FILE, JSON.stringify({ events: [] }, null, 2), 'utf8');
    }
  }

  async getCalendar() {
    await this.ensureCalendarFile();
    const data = await fs.readFile(CALENDAR_FILE, 'utf8');
    return JSON.parse(data);
  }

  async saveCalendar(calendar) {
    await fs.writeFile(CALENDAR_FILE, JSON.stringify(calendar, null, 2), 'utf8');
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'add_event',
          description: 'Add a new event to the calendar',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the event',
              },
              date: {
                type: 'string',
                description: 'Date of the event (YYYY-MM-DD)',
              },
              time: {
                type: 'string',
                description: 'Time of the event (HH:MM)',
              },
              description: {
                type: 'string',
                description: 'Description of the event',
              },
            },
            required: ['title', 'date'],
          },
        },
        {
          name: 'list_events',
          description: 'List events for a specific date or date range',
          inputSchema: {
            type: 'object',
            properties: {
              date: {
                type: 'string',
                description: 'Date to list events for (YYYY-MM-DD)',
              },
              start_date: {
                type: 'string',
                description: 'Start date for range (YYYY-MM-DD)',
              },
              end_date: {
                type: 'string',
                description: 'End date for range (YYYY-MM-DD)',
              },
            },
          },
        },
        {
          name: 'delete_event',
          description: 'Delete an event from the calendar',
          inputSchema: {
            type: 'object',
            properties: {
              event_id: {
                type: 'string',
                description: 'ID of the event to delete',
              },
            },
            required: ['event_id'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'add_event': {
          const { title, date, time, description } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          const newEvent = {
            id: Date.now().toString(),
            title,
            date,
            time: time || '',
            description: description || '',
            created_at: new Date().toISOString(),
          };
          
          calendar.events.push(newEvent);
          await this.saveCalendar(calendar);
          
          return {
            content: [
              {
                type: 'text',
                text: `Событие "${title}" добавлено в календарь на ${date}${time ? ' в ' + time : ''}.`,
              },
            ],
          };
        }
        
        case 'list_events': {
          const { date, start_date, end_date } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          let filteredEvents = [];
          
          if (date) {
            // Фильтрация по конкретной дате
            filteredEvents = calendar.events.filter(event => event.date === date);
          } else if (start_date && end_date) {
            // Фильтрация по диапазону дат
            filteredEvents = calendar.events.filter(event => {
              return event.date >= start_date && event.date <= end_date;
            });
          } else {
            // Возвращаем все события
            filteredEvents = calendar.events;
          }
          
          // Сортировка по дате и времени
          filteredEvents.sort((a, b) => {
            if (a.date !== b.date) return a.date.localeCompare(b.date);
            return a.time.localeCompare(b.time);
          });
          
          if (filteredEvents.length === 0) {
            return {
              content: [
                {
                  type: 'text',
                  text: 'Нет событий для отображения.',
                },
              ],
            };
          }
          
          const eventsText = filteredEvents.map(event => {
            return `ID: ${event.id}\nДата: ${event.date}${event.time ? ' ' + event.time : ''}\nНазвание: ${event.title}${event.description ? '\nОписание: ' + event.description : ''}`;
          }).join('\n\n');
          
          return {
            content: [
              {
                type: 'text',
                text: `События:\n\n${eventsText}`,
              },
            ],
          };
        }
        
        case 'delete_event': {
          const { event_id } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          const eventIndex = calendar.events.findIndex(event => event.id === event_id);
          
          if (eventIndex === -1) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Событие с ID ${event_id} не найдено.`,
                },
              ],
              isError: true,
            };
          }
          
          const deletedEvent = calendar.events[eventIndex];
          calendar.events.splice(eventIndex, 1);
          await this.saveCalendar(calendar);
          
          return {
            content: [
              {
                type: 'text',
                text: `Событие "${deletedEvent.title}" удалено из календаря.`,
              },
            ],
          };
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Calendar MCP server running on stdio');
  }
}

const server = new CalendarServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Добавь встречу с клиентом на 15 апреля в 14:00"
ИИ: [использует MCP-сервер] "Событие 'Встреча с клиентом' добавлено в календарь на 2025-04-15 в 14:00."

Пользователь: "Покажи мои события на апрель"
ИИ: [использует MCP-сервер] "События:

ID: 1710561723456
Дата: 2025-04-15 14:00
Название: Встреча с клиентом"

Пример 7: Сервер для работы с задачами (TODO-лист)

MCP-сервер для управления списком задач.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';

const TASKS_FILE = process.env.TASKS_FILE || './tasks.json';

class TasksServer {
  constructor() {
    this.server = new Server(
      {
        name: 'tasks-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureTasksFile() {
    try {
      await fs.access(TASKS_FILE);
    } catch (error) {
      // Файл не существует, создаем его с пустым списком задач
      await fs.writeFile(TASKS_FILE, JSON.stringify({ tasks: [] }, null, 2), 'utf8');
    }
  }

  async getTasks() {
    await this.ensureTasksFile();
    const data = await fs.readFile(TASKS_FILE, 'utf8');
    return JSON.parse(data);
  }

  async saveTasks(tasksData) {
    await fs.writeFile(TASKS_FILE, JSON.stringify(tasksData, null, 2), 'utf8');
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'add_task',
          description: 'Add a new task to the todo list',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the task',
              },
              priority: {
                type: 'string',
                description: 'Priority of the task (low, medium, high)',
                enum: ['low', 'medium', 'high'],
              },
              due_date: {
                type: 'string',
                description: 'Due date for the task (YYYY-MM-DD)',
              },
            },
            required: ['title'],
          },
        },
        {
          name: 'list_tasks',
          description: 'List all tasks or filter by status',
          inputSchema: {
            type: 'object',
            properties: {
              status: {
                type: 'string',
                description: 'Filter tasks by status (pending, completed)',
                enum: ['pending', 'completed'],
              },
              priority: {
                type: 'string',
                description: 'Filter tasks by priority (low, medium, high)',
                enum: ['low', 'medium', 'high'],
              },
            },
          },
        },
        {
          name: 'complete_task',
          description: 'Mark a task as completed',
          inputSchema: {
            type: 'object',
            properties: {
              task_id: {
                type: 'string',
                description: 'ID of the task to mark as completed',
              },
            },
            required: ['task_id'],
          },
        },
        {
          name: 'delete_task',
          description: 'Delete a task from the todo list',
          inputSchema: {
            type: 'object',
            properties: {
              task_id: {
                type: 'string',
                description: 'ID of the task to delete',
              },
            },
            required: ['task_id'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'add_task': {
          const { title, priority = 'medium', due_date } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          const newTask = {
            id: Date.now().toString(),
            title,
            priority,
            due_date: due_date || null,
            status: 'pending',
            created_at: new Date().toISOString(),
          };
          
          tasksData.tasks.push(newTask);
          await this.saveTasks(tasksData);
          
          return {
            content: [
              {
                type: 'text',
                text: `Задача "${title}" добавлена в список.`,
              },
            ],
          };
        }
        
        case 'list_tasks': {
          const { status, priority } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          let filteredTasks = tasksData.tasks;
          
          if (status) {
            filteredTasks = filteredTasks.filter(task => task.status === status);
          }
          
          if (priority) {
            filteredTasks = filteredTasks.filter(task => task.priority === priority);
          }
          
          // Сортировка по приоритету и дате создания
          const priorityOrder = { high: 0, medium: 1, low: 2 };
          filteredTasks.sort((a, b) => {
            if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
              return priorityOrder[a.priority] - priorityOrder[b.priority];
            }
            return new Date(a.created_at) - new Date(b.created_at);
          });
          
          if (filteredTasks.length === 0) {
            return {
              content: [
                {
                  type: 'text',
                  text: 'Нет задач для отображения.',
                },
              ],
            };
          }
          
          const tasksText = filteredTasks.map(task => {
            const statusEmoji = task.status === 'completed' ? '✅' : '⏳';
            const priorityEmoji = {
              low: '🟢',
              medium: '🟡',
              high: '🔴',
            }[task.priority];
            
            return `${statusEmoji} ${priorityEmoji} [${task.id}] ${task.title}${task.due_date ? ' (до ' + task.due_date + ')' : ''}`;
          }).join('\n');
          
          return {
            content: [
              {
                type: 'text',
                text: `Задачи:\n\n${tas
# Всеобъемлющее руководство по MCP-серверам

## Содержание

1. [Введение в MCP](#введение-в-mcp)
2. [Что такое MCP-серверы?](#что-такое-mcp-серверы)
3. [Основные концепции](#основные-концепции)
4. [Создание вашего первого MCP-сервера](#создание-вашего-первого-mcp-сервера)
5. [Примеры использования MCP-серверов](#примеры-использования-mcp-серверов)
6. [Продвинутые возможности](#продвинутые-возможности)
7. [Лучшие практики](#лучшие-практики)
8. [Устранение неполадок](#устранение-неполадок)
9. [Заключение](#заключение)

---

## Введение в MCP

MCP (Model Context Protocol)  это протокол, который позволяет крупным языковым моделям (LLM) и другим интеллектуальным системам взаимодействовать с внешними сервисами и ресурсами. Представьте это как мост, который соединяет мощные ИИ-системы (такие как Claude) с внешним миром  интернетом, базами данных, API и другими источниками информации.

### Почему это важно?

Представьте, что вы разговариваете с умным помощником, но он не может:
- Проверить актуальную погоду
- Найти что-то в интернете 
- Взаимодействовать с вашими файлами
- Использовать калькулятор
- Отправить email

Это существенно ограничивает его полезность. MCP решает эту проблему, позволяя ИИ-системам обращаться к внешним сервисам и расширять свои возможности.

### Простая аналогия

Представьте MCP как "USB-порт для ИИ". Так же как USB-порт позволяет подключать к компьютеру разные устройства (камеры, мышки, принтеры), MCP позволяет подключать к ИИ разные инструменты и сервисы.

---

## Что такое MCP-серверы?

MCP-сервер  это программа, которая:

1. **Предоставляет определенную функциональность** (например, доступ к погоде, работу с файлами и т.д.)
2. **Говорит на языке протокола MCP**, чтобы ИИ-системы могли ее использовать
3. **Работает как "мост"** между ИИ-системой и внешними сервисами/API

### Простая аналогия

Если представить, что ИИ  это клиент ресторана, то MCP-серверы  это официанты, которые приносят блюда (информацию/сервисы) с кухни (интернета/внешних API).

### Что могут делать MCP-серверы?

MCP-серверы могут предоставлять ИИ доступ к:
- Поиску в интернете
- Прогнозу погоды
- Новостям
- Базам данных
- Калькуляторам
- Системным операциям
- API сторонних сервисов (Twitter, Spotify, YouTube и т.д.)
- И многому другому!

---

## Основные концепции

Перед тем как мы перейдем к примерам, давайте разберемся с основными концепциями, которые нужно понимать:

### 1. Инструменты (Tools)

**Инструменты**  это функции или операции, которые MCP-сервер может выполнять. Например, "получить прогноз погоды", "найти информацию в Google", "отправить email".

Аналогия: Инструменты — это кнопки на пульте телевизора. Каждая кнопка выполняет определенное действие.


### 2. Ресурсы (Resources)

**Ресурсы** — это данные или информация, к которым MCP-сервер обеспечивает доступ. Например, текущая погода, информация о фильме, данные из базы.

Аналогия: Ресурсы — это книги в библиотеке. ИИ может "взять книгу" и прочитать информацию из нее.


### 3. Шаблоны ресурсов (Resource Templates)

**Шаблоны ресурсов** определяют, как можно обращаться к динамическим ресурсам. Например, шаблон `weather://{город}/current` позволяет запрашивать погоду для любого города.

Аналогия: Шаблон ресурса — это как карточный каталог в библиотеке, который помогает найти нужную книгу по описанию.


### 4. URI (Uniform Resource Identifier)

**URI** — это уникальный идентификатор ресурса, который используется для обращения к конкретному ресурсу. Например, `weather://Moscow/current`.

Аналогия: URI — это как адрес в городе. Он позволяет точно найти нужный "дом" (ресурс).


### 5. Запросы и ответы (Requests & Responses)

MCP работает по модели запрос-ответ:
- ИИ отправляет **запрос** на MCP-сервер (например, "какая погода в Москве?")
- MCP-сервер обрабатывает запрос и отправляет **ответ** (например, "в Москве 22°C, солнечно")

Аналогия: Это как разговор по телефону — вы задаете вопрос, собеседник отвечает.


---

## Создание вашего первого MCP-сервера

Давайте рассмотрим процесс создания простого MCP-сервера шаг за шагом. Не беспокойтесь, если вы не знаете, как программировать — мы рассмотрим общую концепцию, а потом перейдем к готовым примерам.

### Шаг 1: Установка необходимых инструментов

Для создания MCP-сервера вам понадобятся:
- Node.js (среда выполнения JavaScript)
- npm (менеджер пакетов)
- MCP SDK (набор инструментов для работы с MCP)

### Шаг 2: Инициализация проекта

```bash
# Создаем новую директорию
mkdir my-weather-server
cd my-weather-server

# Инициализируем проект
npm init -y

# Устанавливаем MCP SDK и другие необходимые пакеты
npm install @modelcontextprotocol/sdk axios

Шаг 3: Создание простого MCP-сервера

Создадим файл index.js с базовым кодом MCP-сервера:

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

// Создаем экземпляр сервера
const server = new Server(
  {
    name: 'hello-world-server',
    version: '0.1.0',
  },
  {
    capabilities: {
      resources: {},
      tools: {},
    },
  }
);

// Добавляем обработчик ошибок
server.onerror = (error) => console.error('[MCP Error]', error);

// Запускаем сервер
const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);
console.error('Hello World MCP server running on stdio');

Шаг 4: Сборка и запуск

# Компилируем код (если необходимо)
npm run build

# Запускаем сервер
node index.js

Шаг 5: Конфигурация MCP-сервера

Чтобы ИИ-система могла использовать ваш MCP-сервер, необходимо добавить его в конфигурационный файл MCP:

{
  "mcpServers": {
    "hello-world": {
      "command": "node",
      "args": ["/путь/к/вашему/серверу/index.js"],
      "env": {}
    }
  }
}

Вот и всё! Теперь у вас есть простой MCP-сервер. Конечно, он пока ничего не делает, но это основа, на которой мы будем строить наши примеры.


Примеры использования MCP-серверов

Теперь давайте рассмотрим конкретные примеры MCP-серверов — от самых простых до сложных и специализированных.

Пример 1: "Hello World" MCP-сервер

Самый простой MCP-сервер, который просто возвращает приветствие.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';

class HelloWorldServer {
  constructor() {
    this.server = new Server(
      {
        name: 'hello-world-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'say_hello',
          description: 'Returns a friendly greeting',
          inputSchema: {
            type: 'object',
            properties: {
              name: {
                type: 'string',
                description: 'Name of the person to greet',
              }
            },
            required: ['name'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'say_hello') {
        const name = request.params.arguments.name;
        return {
          content: [
            {
              type: 'text',
              text: `Привет, ${name}! Как дела?`,
            },
          ],
        };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Hello World MCP server running on stdio');
  }
}

const server = new HelloWorldServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Поздоровайся со мной"
ИИ: [использует MCP-сервер] "Привет, [имя]! Как дела?"

Пример 2: Сервер погоды

MCP-сервер, который предоставляет информацию о погоде.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ListToolsRequestSchema,
  McpError,
  ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';

const API_KEY = process.env.OPENWEATHER_API_KEY;
if (!API_KEY) {
  throw new Error('OPENWEATHER_API_KEY environment variable is required');
}

class WeatherServer {
  constructor() {
    this.server = new Server(
      {
        name: 'weather-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.axiosInstance = axios.create({
      baseURL: 'http://api.openweathermap.org/data/2.5',
      params: {
        appid: API_KEY,
        units: 'metric',
      },
    });

    this.setupResourceHandlers();
    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupResourceHandlers() {
    this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
      resourceTemplates: [
        {
          uriTemplate: 'weather://{city}/current',
          name: 'Current weather for a given city',
          mimeType: 'application/json',
          description: 'Real-time weather data for a specified city',
        },
      ],
    }));

    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const match = request.params.uri.match(/^weather:\/\/([^/]+)\/current$/);
      if (!match) {
        throw new McpError(
          ErrorCode.InvalidRequest,
          `Invalid URI format: ${request.params.uri}`
        );
      }
      const city = decodeURIComponent(match[1]);

      try {
        const response = await this.axiosInstance.get('weather', {
          params: { q: city },
        });

        return {
          contents: [
            {
              uri: request.params.uri,
              mimeType: 'application/json',
              text: JSON.stringify(
                {
                  temperature: response.data.main.temp,
                  conditions: response.data.weather[0].description,
                  humidity: response.data.main.humidity,
                  wind_speed: response.data.wind.speed,
                  timestamp: new Date().toISOString(),
                },
                null,
                2
              ),
            },
          ],
        };
      } catch (error) {
        if (axios.isAxiosError(error)) {
          throw new McpError(
            ErrorCode.InternalError,
            `Weather API error: ${error.response?.data.message ?? error.message}`
          );
        }
        throw error;
      }
    });
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'get_forecast',
          description: 'Get weather forecast for a city',
          inputSchema: {
            type: 'object',
            properties: {
              city: {
                type: 'string',
                description: 'City name',
              },
              days: {
                type: 'number',
                description: 'Number of days (1-5)',
                minimum: 1,
                maximum: 5,
              },
            },
            required: ['city'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'get_forecast') {
        const city = request.params.arguments.city;
        const days = Math.min(request.params.arguments.days || 3, 5);

        try {
          const response = await this.axiosInstance.get('forecast', {
            params: {
              q: city,
              cnt: days * 8,
            },
          });

          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  response.data.list.map(item => ({
                    date: item.dt_txt,
                    temperature: item.main.temp,
                    conditions: item.weather[0].description,
                    humidity: item.main.humidity,
                    wind_speed: item.wind.speed,
                  })),
                  null,
                  2
                ),
              },
            ],
          };
        } catch (error) {
          if (axios.isAxiosError(error)) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Weather API error: ${error.response?.data.message ?? error.message}`,
                },
              ],
              isError: true,
            };
          }
          throw error;
        }
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Weather MCP server running on stdio');
  }
}

const server = new WeatherServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Какая погода в Москве?"
ИИ: [использует MCP-сервер] "В Москве сейчас 22°C, солнечно, влажность 45%, ветер 3 м/с"

Пример 3: Сервер для работы с заметками

MCP-сервер для создания, чтения и управления заметками.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';

const NOTES_DIR = process.env.NOTES_DIR || './notes';

class NotesServer {
  constructor() {
    this.server = new Server(
      {
        name: 'notes-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureNotesDir() {
    try {
      await fs.mkdir(NOTES_DIR, { recursive: true });
    } catch (error) {
      console.error('Error creating notes directory:', error);
    }
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'create_note',
          description: 'Create a new note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note',
              },
              content: {
                type: 'string',
                description: 'Content of the note',
              },
            },
            required: ['title', 'content'],
          },
        },
        {
          name: 'list_notes',
          description: 'List all available notes',
          inputSchema: {
            type: 'object',
            properties: {},
            required: [],
          },
        },
        {
          name: 'read_note',
          description: 'Read a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note to read',
              },
            },
            required: ['title'],
          },
        },
        {
          name: 'delete_note',
          description: 'Delete a specific note',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the note to delete',
              },
            },
            required: ['title'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      await this.ensureNotesDir();

      switch (request.params.name) {
        case 'create_note': {
          const { title, content } = request.params.arguments;
          const filename = this.titleToFilename(title);
          await fs.writeFile(path.join(NOTES_DIR, filename), content, 'utf8');
          return {
            content: [
              {
                type: 'text',
                text: `Note "${title}" created successfully.`,
              },
            ],
          };
        }
        case 'list_notes': {
          const files = await fs.readdir(NOTES_DIR);
          const notes = files
            .filter(file => file.endsWith('.txt'))
            .map(file => this.filenameToTitle(file));
          return {
            content: [
              {
                type: 'text',
                text: notes.length > 0 
                  ? `Available notes:\n${notes.map(note => `- ${note}`).join('\n')}`
                  : 'No notes found.',
              },
            ],
          };
        }
        case 'read_note': {
          const { title } = request.params.arguments;
          const filename = this.titleToFilename(title);
          try {
            const content = await fs.readFile(path.join(NOTES_DIR, filename), 'utf8');
            return {
              content: [
                {
                  type: 'text',
                  text: `# ${title}\n\n${content}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Note "${title}" not found.`,
                },
              ],
              isError: true,
            };
          }
        }
        case 'delete_note': {
          const { title } = request.params.arguments;
          const filename = this.titleToFilename(title);
          try {
            await fs.unlink(path.join(NOTES_DIR, filename));
            return {
              content: [
                {
                  type: 'text',
                  text: `Note "${title}" deleted successfully.`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error deleting note "${title}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  titleToFilename(title) {
    return `${title.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}.txt`;
  }

  filenameToTitle(filename) {
    return filename.replace(/\.txt$/, '').replace(/_/g, ' ');
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Notes MCP server running on stdio');
  }
}

const server = new NotesServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Создай заметку с заголовком 'Покупки' и содержанием 'Молоко, хлеб, яйца'"
ИИ: [использует MCP-сервер] "Заметка 'Покупки' успешно создана."

Пользователь: "Покажи мне список всех заметок"
ИИ: [использует MCP-сервер] "Доступные заметки:
- Покупки"

Пользователь: "Прочитай заметку 'Покупки'"
ИИ: [использует MCP-сервер] "# Покупки

Молоко, хлеб, яйца"

Пример 4: Поисковый сервер

MCP-сервер для поиска информации в интернете.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
import cheerio from 'cheerio';

class SearchServer {
  constructor() {
    this.server = new Server(
      {
        name: 'search-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'search_web',
          description: 'Search the web for information',
          inputSchema: {
            type: 'object',
            properties: {
              query: {
                type: 'string',
                description: 'Search query',
              },
              num_results: {
                type: 'number',
                description: 'Number of results to return',
                minimum: 1,
                maximum: 10,
              },
            },
            required: ['query'],
          },
        },
        {
          name: 'fetch_url',
          description: 'Fetch and summarize the content of a URL',
          inputSchema: {
            type: 'object',
            properties: {
              url: {
                type: 'string',
                description: 'URL to fetch',
              },
            },
            required: ['url'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'search_web': {
          const { query, num_results = 5 } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой поисковой API
            const response = await axios.get('https://api.example.com/search', {
              params: {
                q: query,
                limit: num_results,
              },
              headers: {
                'Authorization': `Bearer ${process.env.SEARCH_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Search results for "${query}":\n\n` +
                    response.data.results.map((result, index) => 
                      `${index + 1}. ${result.title}\n   ${result.url}\n   ${result.snippet}\n`
                    ).join('\n'),
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error searching for "${query}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'fetch_url': {
          const { url } = request.params.arguments;
          
          try {
            const response = await axios.get(url);
            const $ = cheerio.load(response.data);
            
            // Базовое извлечение текста
            const title = $('title').text().trim();
            const metaDescription = $('meta[name="description"]').attr('content') || '';
            
            // Извлечение основного содержимого (это базовая реализация)
            let content = '';
            $('p').each((i, elem) => {
              content += $(elem).text().trim() + '\n\n';
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `# ${title}\n\n${metaDescription}\n\n## Содержание страницы:\n\n${content}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error fetching URL "${url}": ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Search MCP server running on stdio');
  }
}

const server = new SearchServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Найди информацию о солнечной системе"
ИИ: [использует MCP-сервер] "Результаты поиска для 'солнечная система':
1. Солнечная система - Википедия
   https://ru.wikipedia.org/wiki/Солнечная_система
   Солнечная система — планетная система, включающая центральную звезду Солнце и все естественные космические объекты...

2. ..."

Пользователь: "Покажи информацию с сайта о солнечной системе"
ИИ: [использует MCP-сервер fetch_url со ссылкой из результатов поиска]

Пример 5: Сервер переводчик

MCP-сервер для перевода текста между разными языками.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';

const TRANSLATION_API_KEY = process.env.TRANSLATION_API_KEY;
if (!TRANSLATION_API_KEY) {
  throw new Error('TRANSLATION_API_KEY environment variable is required');
}

class TranslationServer {
  constructor() {
    this.server = new Server(
      {
        name: 'translation-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'translate_text',
          description: 'Translate text between languages',
          inputSchema: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'Text to translate',
              },
              source_language: {
                type: 'string',
                description: 'Source language code (e.g., "en", "ru", "fr")',
              },
              target_language: {
                type: 'string',
                description: 'Target language code (e.g., "en", "ru", "fr")',
              },
            },
            required: ['text', 'target_language'],
          },
        },
        {
          name: 'detect_language',
          description: 'Detect the language of a text',
          inputSchema: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'Text to analyze',
              },
            },
            required: ['text'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'translate_text': {
          const { text, source_language, target_language } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой API перевода
            const response = await axios.post('https://api.example.com/translate', {
              text,
              source: source_language || 'auto',
              target: target_language,
            }, {
              headers: {
                'Authorization': `Bearer ${TRANSLATION_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Перевод: ${response.data.translatedText}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error translating text: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'detect_language': {
          const { text } = request.params.arguments;
          
          try {
            // Эта часть условна и зависит от используемой API
            const response = await axios.post('https://api.example.com/detect', {
              text,
            }, {
              headers: {
                'Authorization': `Bearer ${TRANSLATION_API_KEY}`,
              },
            });
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Определенный язык: ${response.data.languageName} (${response.data.languageCode})\nУверенность: ${response.data.confidence * 100}%`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error detecting language: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Translation MCP server running on stdio');
  }
}

const server = new TranslationServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Переведи 'Здравствуйте, как дела?' на английский"
ИИ: [использует MCP-сервер] "Перевод: Hello, how are you?"

Пользователь: "Определи язык текста 'Bonjour, comment allez-vous?'"
ИИ: [использует MCP-сервер] "Определенный язык: Французский (fr)
Уверенность: 98%"

Пример 6: Сервер для работы с календарем

MCP-сервер для управления календарем и событиями.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';

const CALENDAR_FILE = process.env.CALENDAR_FILE || './calendar.json';

class CalendarServer {
  constructor() {
    this.server = new Server(
      {
        name: 'calendar-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureCalendarFile() {
    try {
      await fs.access(CALENDAR_FILE);
    } catch (error) {
      // Файл не существует, создаем его с пустым календарем
      await fs.writeFile(CALENDAR_FILE, JSON.stringify({ events: [] }, null, 2), 'utf8');
    }
  }

  async getCalendar() {
    await this.ensureCalendarFile();
    const data = await fs.readFile(CALENDAR_FILE, 'utf8');
    return JSON.parse(data);
  }

  async saveCalendar(calendar) {
    await fs.writeFile(CALENDAR_FILE, JSON.stringify(calendar, null, 2), 'utf8');
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'add_event',
          description: 'Add a new event to the calendar',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the event',
              },
              date: {
                type: 'string',
                description: 'Date of the event (YYYY-MM-DD)',
              },
              time: {
                type: 'string',
                description: 'Time of the event (HH:MM)',
              },
              description: {
                type: 'string',
                description: 'Description of the event',
              },
            },
            required: ['title', 'date'],
          },
        },
        {
          name: 'list_events',
          description: 'List events for a specific date or date range',
          inputSchema: {
            type: 'object',
            properties: {
              date: {
                type: 'string',
                description: 'Date to list events for (YYYY-MM-DD)',
              },
              start_date: {
                type: 'string',
                description: 'Start date for range (YYYY-MM-DD)',
              },
              end_date: {
                type: 'string',
                description: 'End date for range (YYYY-MM-DD)',
              },
            },
          },
        },
        {
          name: 'delete_event',
          description: 'Delete an event from the calendar',
          inputSchema: {
            type: 'object',
            properties: {
              event_id: {
                type: 'string',
                description: 'ID of the event to delete',
              },
            },
            required: ['event_id'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'add_event': {
          const { title, date, time, description } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          const newEvent = {
            id: Date.now().toString(),
            title,
            date,
            time: time || '',
            description: description || '',
            created_at: new Date().toISOString(),
          };
          
          calendar.events.push(newEvent);
          await this.saveCalendar(calendar);
          
          return {
            content: [
              {
                type: 'text',
                text: `Событие "${title}" добавлено в календарь на ${date}${time ? ' в ' + time : ''}.`,
              },
            ],
          };
        }
        
        case 'list_events': {
          const { date, start_date, end_date } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          let filteredEvents = [];
          
          if (date) {
            // Фильтрация по конкретной дате
            filteredEvents = calendar.events.filter(event => event.date === date);
          } else if (start_date && end_date) {
            // Фильтрация по диапазону дат
            filteredEvents = calendar.events.filter(event => {
              return event.date >= start_date && event.date <= end_date;
            });
          } else {
            // Возвращаем все события
            filteredEvents = calendar.events;
          }
          
          // Сортировка по дате и времени
          filteredEvents.sort((a, b) => {
            if (a.date !== b.date) return a.date.localeCompare(b.date);
            return a.time.localeCompare(b.time);
          });
          
          if (filteredEvents.length === 0) {
            return {
              content: [
                {
                  type: 'text',
                  text: 'Нет событий для отображения.',
                },
              ],
            };
          }
          
          const eventsText = filteredEvents.map(event => {
            return `ID: ${event.id}\nДата: ${event.date}${event.time ? ' ' + event.time : ''}\nНазвание: ${event.title}${event.description ? '\nОписание: ' + event.description : ''}`;
          }).join('\n\n');
          
          return {
            content: [
              {
                type: 'text',
                text: `События:\n\n${eventsText}`,
              },
            ],
          };
        }
        
        case 'delete_event': {
          const { event_id } = request.params.arguments;
          const calendar = await this.getCalendar();
          
          const eventIndex = calendar.events.findIndex(event => event.id === event_id);
          
          if (eventIndex === -1) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Событие с ID ${event_id} не найдено.`,
                },
              ],
              isError: true,
            };
          }
          
          const deletedEvent = calendar.events[eventIndex];
          calendar.events.splice(eventIndex, 1);
          await this.saveCalendar(calendar);
          
          return {
            content: [
              {
                type: 'text',
                text: `Событие "${deletedEvent.title}" удалено из календаря.`,
              },
            ],
          };
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Calendar MCP server running on stdio');
  }
}

const server = new CalendarServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Добавь встречу с клиентом на 15 апреля в 14:00"
ИИ: [использует MCP-сервер] "Событие 'Встреча с клиентом' добавлено в календарь на 2025-04-15 в 14:00."

Пользователь: "Покажи мои события на апрель"
ИИ: [использует MCP-сервер] "События:

ID: 1710561723456
Дата: 2025-04-15 14:00
Название: Встреча с клиентом"

Пример 7: Сервер для работы с задачами (TODO-лист)

MCP-сервер для управления списком задач.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';

const TASKS_FILE = process.env.TASKS_FILE || './tasks.json';

class TasksServer {
  constructor() {
    this.server = new Server(
      {
        name: 'tasks-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async ensureTasksFile() {
    try {
      await fs.access(TASKS_FILE);
    } catch (error) {
      // Файл не существует, создаем его с пустым списком задач
      await fs.writeFile(TASKS_FILE, JSON.stringify({ tasks: [] }, null, 2), 'utf8');
    }
  }

  async getTasks() {
    await this.ensureTasksFile();
    const data = await fs.readFile(TASKS_FILE, 'utf8');
    return JSON.parse(data);
  }

  async saveTasks(tasksData) {
    await fs.writeFile(TASKS_FILE, JSON.stringify(tasksData, null, 2), 'utf8');
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'add_task',
          description: 'Add a new task to the todo list',
          inputSchema: {
            type: 'object',
            properties: {
              title: {
                type: 'string',
                description: 'Title of the task',
              },
              priority: {
                type: 'string',
                description: 'Priority of the task (low, medium, high)',
                enum: ['low', 'medium', 'high'],
              },
              due_date: {
                type: 'string',
                description: 'Due date for the task (YYYY-MM-DD)',
              },
            },
            required: ['title'],
          },
        },
        {
          name: 'list_tasks',
          description: 'List all tasks or filter by status',
          inputSchema: {
            type: 'object',
            properties: {
              status: {
                type: 'string',
                description: 'Filter tasks by status (pending, completed)',
                enum: ['pending', 'completed'],
              },
              priority: {
                type: 'string',
                description: 'Filter tasks by priority (low, medium, high)',
                enum: ['low', 'medium', 'high'],
              },
            },
          },
        },
        {
          name: 'complete_task',
          description: 'Mark a task as completed',
          inputSchema: {
            type: 'object',
            properties: {
              task_id: {
                type: 'string',
                description: 'ID of the task to mark as completed',
              },
            },
            required: ['task_id'],
          },
        },
        {
          name: 'delete_task',
          description: 'Delete a task from the todo list',
          inputSchema: {
            type: 'object',
            properties: {
              task_id: {
                type: 'string',
                description: 'ID of the task to delete',
              },
            },
            required: ['task_id'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'add_task': {
          const { title, priority = 'medium', due_date } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          const newTask = {
            id: Date.now().toString(),
            title,
            priority,
            due_date: due_date || null,
            status: 'pending',
            created_at: new Date().toISOString(),
          };
          
          tasksData.tasks.push(newTask);
          await this.saveTasks(tasksData);
          
          return {
            content: [
              {
                type: 'text',
                text: `Задача "${title}" добавлена в список.`,
              },
            ],
          };
        }
        
        case 'list_tasks': {
          const { status, priority } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          let filteredTasks = tasksData.tasks;
          
          if (status) {
            filteredTasks = filteredTasks.filter(task => task.status === status);
          }
          
          if (priority) {
            filteredTasks = filteredTasks.filter(task => task.priority === priority);
          }
          
          // Сортировка по приоритету и дате создания
          const priorityOrder = { high: 0, medium: 1, low: 2 };
          filteredTasks.sort((a, b) => {
            if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
              return priorityOrder[a.priority] - priorityOrder[b.priority];
            }
            return new Date(a.created_at) - new Date(b.created_at);
          });
          
          if (filteredTasks.length === 0) {
            return {
              content: [
                {
                  type: 'text',
                  text: 'Нет задач для отображения.',
                },
              ],
            };
          }
          
          const tasksText = filteredTasks.map(task => {
            const statusEmoji = task.status === 'completed' ? '✅' : '⏳';
            const priorityEmoji = {
              low: '🟢',
              medium: '🟡',
              high: '🔴',
            }[task.priority];
            
            return `${statusEmoji} ${priorityEmoji} [${task.id}] ${task.title}${task.due_date ? ' (до ' + task.due_date + ')' : ''}`;
          }).join('\n');
          
          return {
            content: [
              {
                type: 'text',
                text: `Задачи:\n\n${tasksText}`,
              },
            ],
          };
        }
        
        case 'complete_task': {
          const { task_id } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          const taskIndex = tasksData.tasks.findIndex(task => task.id === task_id);
          
          if (taskIndex === -1) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Задача с ID ${task_id} не найдена.`,
                },
              ],
              isError: true,
            };
          }
          
          tasksData.tasks[taskIndex].status = 'completed';
          await this.saveTasks(tasksData);
          
          return {
            content: [
              {
                type: 'text',
                text: `Задача "${tasksData.tasks[taskIndex].title}" отмечена как выполненная.`,
              },
            ],
          };
        }
        
        case 'delete_task': {
          const { task_id } = request.params.arguments;
          const tasksData = await this.getTasks();
          
          const taskIndex = tasksData.tasks.findIndex(task => task.id === task_id);
          
          if (taskIndex === -1) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Задача с ID ${task_id} не найдена.`,
                },
              ],
              isError: true,
            };
          }
          
          const deletedTask = tasksData.tasks[taskIndex];
          tasksData.tasks.splice(taskIndex, 1);
          await this.saveTasks(tasksData);
          
          return {
            content: [
              {
                type: 'text',
                text: `Задача "${deletedTask.title}" удалена из списка.`,
              },
            ],
          };
        }
        
        default:
          return {
            content: [
              {
                type: 'text',
                text: `Unknown tool: ${request.params.name}`,
              },
            ],
            isError: true,
          };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Tasks MCP server running on stdio');
  }
}

const server = new TasksServer();
server.run().catch(console.error);

Как использовать:

Пользователь: "Добавь задачу 'Купить продукты' с высоким приоритетом"
ИИ: [использует MCP-сервер] "Задача 'Купить продукты' добавлена в список."

Пользователь: "Покажи мои задачи"
ИИ: [использует MCP-сервер] "Задачи:

🔴 ⏳ [1710561823456] Купить продукты"

Пользователь: "Отметь задачу с ID 1710561823456 как выполненную"
ИИ: [использует MCP-сервер] "Задача 'Купить продукты' отмечена как выполненная."

Пример 8: Сервер для работы с базой данных

MCP-сервер для выполнения запросов к базе данных.

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';

const DB_PATH = process.env.DB_PATH || './database.sqlite';

class DatabaseServer {
  constructor() {
    this.server = new Server(
      {
        name: 'database-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    
    this.server.onerror = (error) => console.error('[MCP Error]', error);
  }

  async getDbConnection() {
    if (!this.db) {
      this.db = await open({
        filename: DB_PATH,
        driver: sqlite3.Database,
      });
    }
    return this.db;
  }

  setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'execute_query',
          description: 'Execute an SQL query on the database',
          inputSchema: {
            type: 'object',
            properties: {
              query: {
                type: 'string',
                description: 'SQL query to execute',
              },
              params: {
                type: 'array',
                description: 'Parameters for the query',
                items: {
                  type: ['string', 'number', 'boolean', 'null'],
                },
              },
            },
            required: ['query'],
          },
        },
        {
          name: 'get_tables',
          description: 'Get a list of tables in the database',
          inputSchema: {
            type: 'object',
            properties: {},
            required: [],
          },
        },
        {
          name: 'get_table_schema',
          description: 'Get the schema for a specific table',
          inputSchema: {
            type: 'object',
            properties: {
              table_name: {
                type: 'string',
                description: 'Name of the table',
              },
            },
            required: ['table_name'],
          },
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const db = await this.getDbConnection();

      switch (request.params.name) {
        case 'execute_query': {
          const { query, params = [] } = request.params.arguments;
          
          try {
            // Проверка, является ли запрос SELECT или нет
            const isSelect = query.trim().toUpperCase().startsWith('SELECT');
            
            let result;
            if (isSelect) {
              result = await db.all(query, params);
            } else {
              result = await db.run(query, params);
            }
            
            return {
              content: [
                {
                  type: 'text',
                  text: isSelect 
                    ? `Результаты запроса:\n\n${JSON.stringify(result, null, 2)}`
                    : `Запрос выполнен успешно. Затронуто строк: ${result.changes || 0}`,
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Ошибка выполнения запроса: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'get_tables': {
          try {
            const result = await db.all("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'");
            const tables = result.map(row => row.name);
            
            return {
              content: [
                {
                  type: 'text',
                  text: tables.length > 0
                    ? `Таблицы в базе данных:\n\n${tables.join('\n')}`
                    : 'В базе данных нет таблиц.',
                },
              ],
            };
          } catch (error) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Ошибка получения списка таблиц: ${error.message}`,
                },
              ],
              isError: true,
            };
          }
        }
        
        case 'get_table_schema': {
          const { table_name } = request.params.arguments;
          
          try {
            const result = await db.all(`PRAGMA table_info(${table_name})`);
            
            if (result.length === 0) {
              return {
                content: [
                  {
                    type: 'text',
                    text: `Таблица '${table_name}' не найдена.`,
                  },
                ],
                isError: true,
              };
            }
            
            const columns = result.map(col => {
              return `${col.name} (${col.type})${col.pk ? ' PRIMARY KEY' : ''}${col.notnull ? ' NOT NULL' : ''}`;
            });
            
            return {
              content: [
                {
                  type: 'text',
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment