Skip to main content
Glama
aleks-yustas

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