Yandex Weather MCP
by aleks-yustas
README.md
# Yandex Weather MCP
Локальный MCP-сервер для получения данных из API Яндекс Погоды по координатам. Сервер прячет API-ключ от MCP-клиента, валидирует входные параметры, нормализует ответ API и кэширует запросы на 5-15 минут.
## Технический консилиум
Backend-разработчик: на первом этапе нужны четыре инструмента MCP: текущая погода, прогноз, короткая сводка и сравнение двух точек. Запрашивать стоит координаты, язык, лимит дней, почасовой прогноз и расширенные поля. Пользователю полезны температура, ощущается как, состояние, ветер, влажность, давление, осадки, рассвет/закат и служебный признак кэша.
DevOps-инженер: локальный Docker-запуск должен получать секреты только через env, работать не от root и иметь простой healthcheck. Риски: stdio MCP плохо сочетается с обычной HTTP-проверкой, `.env` легко случайно закоммитить, а сеть из контейнера должна иметь доступ к API.
Специалист по API-интеграциям: клиенту нужен таймаут, понятная обработка 401/403/404/429/400, аккуратный JSON parsing и endpoint, который можно переопределить через env. Для первого этапа не стоит делать агрессивные retry: погодный запрос безопасен, но повтор при 429 только быстрее сожжет лимит.
Специалист по MCP: инструменты лучше возвращать JSON как `text` content, потому что это совместимо с большинством MCP-клиентов и удобно для отладки. Ошибки возвращаются структурированно в `{ error: { code, message, details } }`.
Специалист по типизации и качеству: TypeScript + Zod дают строгие контракты на входе и нормализацию на выходе. В тестах нужно закрыть координаты, маппинг состояний, отсутствующий ключ, ошибки API, кэш и нормализацию.
## Архитектурное решение
Для первого этапа выбран TypeScript / Node.js. Python быстрее для прототипа, Rust надежнее для production-бинарника, но TypeScript дает лучший баланс MCP-экосистемы, строгих типов, Docker-упаковки и скорости разработки.
Проект устроен так:
```text
src/
index.ts
server.ts
config.ts
yandexWeatherClient.ts
tools/
schemas/
utils/
Dockerfile
docker-compose.yml
README.md
.env.example
```
По документации: публично используемый endpoint Яндекс Погоды для прогноза обычно выглядит как `https://api.weather.yandex.ru/v2/forecast` с ключом в заголовке `X-Yandex-API-Key`. В коде endpoint вынесен в `YANDEX_WEATHER_API_URL`, чтобы без изменения сервера перейти на endpoint вашего тарифа, если в личном кабинете доступна другая версия.
## Настройка
Получите API-ключ в кабинете Яндекс Погоды / Яндекс API, затем создайте `.env`:
```bash
cp .env.example .env
```
Заполните:
```env
YANDEX_WEATHER_API_KEY=your_api_key_here
YANDEX_WEATHER_LANG=ru_RU
CACHE_TTL_SECONDS=600
LOG_LEVEL=info
MCP_TRANSPORT=http
HOST=0.0.0.0
PORT=3000
```
Не храните настоящий ключ в коде, Dockerfile или README.
## Локальный запуск
```bash
npm install
npm run build
npm start
```
Для разработки:
```bash
npm run dev
```
## Docker
Для серверного запуска используется HTTP transport MCP на `/mcp`. Создайте `.env` перед запуском:
```bash
cp .env.example .env
```
Заполните `YANDEX_WEATHER_API_KEY`, затем запустите:
```bash
docker compose up --build
```
Проверка здоровья:
```bash
curl http://localhost:3000/health
```
MCP endpoint:
```text
http://localhost:3000/mcp
```
Если нужен локальный stdio-режим для MCP-клиента, запускайте Node напрямую или переопределите `MCP_TRANSPORT=stdio`:
```json
{
"mcpServers": {
"yandex-weather": {
"command": "npm",
"args": ["start"],
"env": {
"YANDEX_WEATHER_API_KEY": "your_api_key_here"
}
}
}
}
```
## MCP tools
### `get_current_weather`
Вход:
```json
{
"lat": 55.7558,
"lon": 37.6173,
"lang": "ru_RU"
}
```
Возвращает `location`, `current` и `meta`.
### `get_weather_forecast`
Вход:
```json
{
"lat": 55.7558,
"lon": 37.6173,
"lang": "ru_RU",
"days": 3
}
```
Возвращает прогноз по дням, частям суток и почасовым данным, если они доступны для тарифа/API.
### `get_weather_summary`
Вход:
```json
{
"lat": 55.7558,
"lon": 37.6173
}
```
Пример ответа:
```json
{
"summary": "Сейчас +18°C, ощущается как +16°C. Пасмурно, ветер 4 м/с, влажность 78%.",
"advice": "Вероятность осадков повышенная, стоит взять зонт или дождевик."
}
```
### `compare_weather`
Вход:
```json
{
"first": { "lat": 55.7558, "lon": 37.6173, "name": "Москва" },
"second": { "lat": 59.9386, "lon": 30.3141, "name": "Санкт-Петербург" },
"lang": "ru_RU"
}
```
Возвращает две нормализованные погодные карточки и сравнение по температуре, ветру и влажности.
## Деплой на VDS
### Требования
- Docker 24+ и Docker Compose V2 (`docker compose`)
- Открытый порт на сервере (например 3000) или Nginx как reverse proxy
### Шаги
```bash
# 1. Клонировать репозиторий на сервер
git clone <url> yandex-weather-mcp
cd yandex-weather-mcp
# 2. Создать директорию для базы данных планировщика
mkdir -p data
# 3. Создать .env и вписать ключ
cp .env.example .env
nano .env # выставить YANDEX_WEATHER_API_KEY и, если нужно, PORT
# 4. Запустить
docker compose up -d --build
# 5. Проверить
curl http://localhost:3000/health
```
После этого MCP endpoint доступен на `http://<vds-ip>:3000/mcp`.
### Nginx reverse proxy (рекомендуется)
Позволяет закрыть прямой доступ к порту 3000 и выставить наружу только 443/80.
```nginx
server {
listen 443 ssl;
server_name weather-mcp.example.com;
ssl_certificate /etc/letsencrypt/live/weather-mcp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/weather-mcp.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 30s;
}
}
```
После этого можно держать порт 3000 закрытым в firewall и обращаться к `https://weather-mcp.example.com/mcp`.
### Firewall
Если Nginx не используется и порт открыт напрямую — ограничьте доступ по IP:
```bash
# UFW пример
ufw allow from <trusted-ip> to any port 3000
ufw deny 3000
```
### Управление контейнером
```bash
# Посмотреть логи (включая тики планировщика)
docker compose logs -f
# Перезапустить
docker compose restart
# Обновить после git pull
git pull && docker compose up -d --build
# Остановить (данные БД сохраняются в ./data/)
docker compose down
# Остановить и удалить данные БД
docker compose down && rm -rf data/
```
Контейнер настроен на `restart: unless-stopped` — поднимается автоматически после перезагрузки сервера.
### Персистентность базы данных планировщика
Планировщик хранит историю наблюдений в SQLite (`weather.db`). Без volume файл живёт внутри контейнера и пропадает при `docker compose down` или пересборке образа.
`docker-compose.yml` уже настроен на монтирование `./data/weather.db` — достаточно создать директорию перед первым запуском:
```bash
mkdir -p data
```
Путь к файлу управляется переменной `WEATHER_DB_PATH` в `.env`. Менять не нужно, если устраивает `./data/weather.db`.
### MCP endpoint для клиента
```json
{
"mcpServers": {
"yandex-weather": {
"url": "http://<vds-ip>:3000/mcp"
}
}
}
```
Или с Nginx:
```json
{
"mcpServers": {
"yandex-weather": {
"url": "https://weather-mcp.example.com/mcp"
}
}
}
```
## Ошибки
Ошибки возвращаются в едином формате:
```json
{
"error": {
"code": "AUTH_ERROR",
"message": "Yandex Weather API rejected the API key",
"details": {}
}
}
```
Поддержанные коды: `CONFIGURATION_ERROR`, `VALIDATION_ERROR`, `AUTH_ERROR`, `RATE_LIMITED`, `NOT_FOUND`, `TIMEOUT`, `NETWORK_ERROR`, `INVALID_JSON`, `UNEXPECTED_RESPONSE`, `UNSUPPORTED_API_PARAMETER`, `YANDEX_API_ERROR`.
## Тестирование
```bash
npm test
npm run typecheck
```
Тесты покрывают:
- валидацию координат;
- маппинг погодных состояний;
- отсутствующий API-ключ;
- ошибку API;
- работу кэша;
- нормализацию ответа Яндекс Погоды.
## Ограничения
- Сервер не делает геокодинг по названию города.
- Набор полей зависит от тарифа и endpoint Яндекс Погоды.
- Кэш хранится только в памяти процесса.
- HTTP healthcheck проверяет живой процесс и конфигурацию, но не дергает внешний API Яндекс Погоды.
## Roadmap второго этапа
- геокодинг по названию города;
- хранение избранных локаций;
- Prometheus-метрики;
- Grafana dashboard;
- rate limiting;
- persistent cache через SQLite/Redis;
- поддержка нескольких weather-провайдеров;
- алерты: дождь, мороз, сильный ветер;
- интеграция с Home Assistant.
TDQS
B3.1/5.0
Scored across 4 tools
Disambiguation5/5
Each tool has a distinct purpose: current weather, forecast, comparison between two locations, and a human-readable summary. No overlapping functionality.
Naming Consistency4/5
Three tools follow the 'get_weather_*' pattern, while 'compare_weather' uses a different verb but still follows verb_noun convention. The naming is mostly consistent.
Tool Count5/5
Four tools is an appropriate count for a weather server, covering core needs without being excessive or sparse.
Completeness4/5
Covers essential weather operations (current, forecast, comparison, summary). Missing historical data or alerts, but these are minor gaps that agents can work around.
Maintenance
ActivityInactive
ResponsivenessNo issues