# Лучшие практики серверов MCP ## Краткий справочник ### Именование серверов - **Python**: `{service}_mcp` (например, `slack_mcp`) - **Node/TypeScript**: `{service}-mcp-server` (например, `slack-mcp-server`) ### Именование инструментов - Используй snake_case с префиксом сервиса - Формат: `{service}_{action}_{resource}` - Пример: `slack_send_message`, `github_create_issue` ### Форматы ответов - Поддерживай и JSON, и Markdown - JSON для программной обработки - Markdown для удобства чтения человеком ### Пагинация - Всегда учитывай параметр `limit` - Возвращай `has_more`, `next_offset`, `total_count` - По умолчанию — 20–50 элементов ### Транспорт - **Потоковый HTTP**: для удалённых серверов и сценариев с несколькими клиентами - **stdio**: для локальных интеграций и инструментов командной строки - Избегай SSE (устарел в пользу потокового HTTP) --- ## Соглашения об именовании серверов Следуй этим стандартизированным шаблонам именования: **Python**: используй формат `{service}_mcp` (нижний регистр с подчёркиваниями) - Примеры: `slack_mcp`, `github_mcp`, `jira_mcp` **Node/TypeScript**: используй формат `{service}-mcp-server` (нижний регистр с дефисами) - Примеры: `slack-mcp-server`, `github-mcp-server`, `jira-mcp-server` Имя должно быть общим, описывать интегрируемый сервис, легко выводиться из описания задачи и не содержать номеров версий. --- ## Именование и проектирование инструментов ### Именование инструментов 1. **Используй snake_case**: `search_users`, `create_project`, `get_channel_info` 2. **Включай префикс сервиса**: учитывай, что твой сервер MCP может использоваться наряду с другими серверами MCP - Используй `slack_send_message` вместо просто `send_message` - Используй `github_create_issue` вместо просто `create_issue` 3. **Ориентируйся на действия**: начинай с глаголов (get, list, search, create и т. д.) 4. **Будь конкретен**: избегай общих имён, которые могут конфликтовать с другими серверами ### Проектирование инструментов - Описания инструментов должны узко и однозначно описывать функциональность - Описания должны точно соответствовать реальной функциональности - Предоставляй аннотации инструментов (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) - Делай операции инструментов сфокусированными и атомарными --- ## Форматы ответов Все инструменты, возвращающие данные, должны поддерживать несколько форматов: ### Формат JSON (`response_format="json"`) - Машиночитаемые структурированные данные - Включай все доступные поля и метаданные - Единообразные имена и типы полей - Используй для программной обработки ### Формат Markdown (`response_format="markdown"`, обычно по умолчанию) - Форматированный текст, удобный для чтения человеком - Используй заголовки, списки и форматирование для ясности - Преобразовывай временные метки в удобочитаемый формат - Показывай отображаемые имена с идентификаторами в скобках - Опускай многословные метаданные --- ## Пагинация Для инструментов, перечисляющих ресурсы: - **Всегда учитывай параметр `limit`** - **Реализуй пагинацию**: используй `offset` или пагинацию на основе курсора - **Возвращай метаданные пагинации**: включай `has_more`, `next_offset`/`next_cursor`, `total_count` - **Никогда не загружай все результаты в память**: особенно важно для больших наборов данных - **По умолчанию используй разумные лимиты**: обычно 20–50 элементов Пример ответа с пагинацией: ```json { "total": 150, "count": 20, "offset": 0, "items": [...], "has_more": true, "next_offset": 20 } ``` --- ## Варианты транспорта ### Потоковый HTTP **Лучше всего подходит для**: удалённых серверов, веб-сервисов, сценариев с несколькими клиентами **Характеристики**: - Двунаправленная связь по HTTP - Поддерживает несколько одновременных клиентов - Может развёртываться как веб-сервис - Позволяет отправлять уведомления от сервера клиенту **Используй, когда**: - Обслуживаешь нескольких клиентов одновременно - Развёртываешь как облачный сервис - Интегрируешь с веб-приложениями ### stdio **Лучше всего подходит для**: локальных интеграций, инструментов командной строки **Характеристики**: - Связь через стандартные потоки ввода-вывода - Простая настройка, конфигурация сети не требуется - Запускается как подпроцесс клиента **Используй, когда**: - Создаёшь инструменты для локальных сред разработки - Интегрируешь с настольными приложениями - Работаешь со сценариями одного пользователя и одной сессии **Примечание**: серверы stdio НЕ должны писать журналы в stdout (используй stderr для журналирования) ### Выбор транспорта | Критерий | stdio | Потоковый HTTP | |-----------|-------|-----------------| | **Развёртывание** | Локальное | Удалённое | | **Клиенты** | Один | Несколько | | **Сложность** | Низкая | Средняя | | **Реальное время** | Нет | Да | --- ## Лучшие практики безопасности ### Аутентификация и авторизация **OAuth 2.1**: - Используй безопасный OAuth 2.1 с сертификатами от признанных центров сертификации - Проверяй токены доступа до обработки запросов - Принимай только токены, специально предназначенные для твоего сервера **Ключи API**: - Храни ключи API в переменных окружения, никогда не в коде - Проверяй ключи при запуске сервера - Предоставляй ясные сообщения об ошибках при сбое аутентификации ### Проверка входных данных - Очищай пути к файлам для предотвращения обхода каталогов - Проверяй URL и внешние идентификаторы - Проверяй размеры и диапазоны параметров - Предотвращай внедрение команд в системные вызовы - Используй проверку по схемам (Pydantic/Zod) для всех входных данных ### Обработка ошибок - Не раскрывай клиентам внутренние ошибки - Записывай связанные с безопасностью ошибки в журналы на стороне сервера - Предоставляй полезные сообщения об ошибках, не раскрывающие лишнего - Освобождай ресурсы после ошибок ### Защита от DNS rebinding Для серверов потокового HTTP, работающих локально: - Включи защиту от DNS rebinding - Проверяй заголовок `Origin` во всех входящих соединениях - Привязывайся к `127.0.0.1`, а не к `0.0.0.0` --- ## Аннотации инструментов Предоставляй аннотации, помогающие клиентам понять поведение инструмента: | Аннотация | Тип | По умолчанию | Описание | |-----------|------|---------|-------------| | `readOnlyHint` | boolean | false | Инструмент не изменяет свою среду | | `destructiveHint` | boolean | true | Инструмент может выполнять разрушающие обновления | | `idempotentHint` | boolean | false | Повторные вызовы с теми же аргументами не дают дополнительного эффекта | | `openWorldHint` | boolean | true | Инструмент взаимодействует с внешними сущностями | **Важно**: аннотации — это подсказки, а не гарантии безопасности. Клиенты не должны принимать критически важные для безопасности решения только на основе аннотаций. --- ## Обработка ошибок - Используй стандартные коды ошибок JSON-RPC - Сообщай об ошибках инструментов внутри объектов результата (а не как об ошибках уровня протокола) - Предоставляй полезные, конкретные сообщения об ошибках с предложением дальнейших действий - Не раскрывай внутренние детали реализации - Корректно освобождай ресурсы при ошибках Пример обработки ошибок: ```typescript try { const result = performOperation(); return { content: [{ type: "text", text: result }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error: ${error.message}. Try using filter='active_only' to reduce results.` }] }; } ``` --- ## Требования к тестированию Всестороннее тестирование должно охватывать: - **Функциональное тестирование**: проверяй корректное выполнение с допустимыми/недопустимыми входными данными - **Интеграционное тестирование**: проверяй взаимодействие с внешними системами - **Тестирование безопасности**: проверяй аутентификацию, очистку входных данных, ограничение частоты запросов - **Тестирование производительности**: проверяй поведение под нагрузкой, тайм-ауты - **Обработку ошибок**: обеспечивай корректное сообщение об ошибках и освобождение ресурсов --- ## Требования к документации - Предоставляй ясную документацию по всем инструментам и возможностям - Включай работающие примеры (не менее 3 на каждую основную функцию) - Документируй аспекты безопасности - Указывай необходимые разрешения и уровни доступа - Документируй ограничения частоты запросов и характеристики производительности