ss-ge-mcp
# ss-ge-mcp
MCP-сервер над [home.ss.ge](https://home.ss.ge)
Прозрачный stateless-прокси: один вызов инструмента = один-несколько живых запросов. Между вызовами не хранится ничего, кроме токена (TTL 1 час) и гео-справочника.
У ss.ge нет публичного API. Контракт восстановлен реверс-инжинирингом и проверен живыми запросами -- разбор в [`docs/API.md`](docs/API.md).
## Установка
```bash
claude mcp add ss-ge --scope user -- npx -y ss-ge-mcp@1
```
Нужен Node >= 20. Клонировать и ставить вручную ничего не надо: `npx` сам скачает пакет
из npm и закеширует. `@1` фиксирует мажорную версию -- обновления внутри неё приезжают
сами, ломающий релиз молча не подменит сервер.
Для клиентов с JSON-конфигом (Claude Desktop, Cursor, Windsurf):
```json
{
"mcpServers": {
"ss-ge": {
"command": "npx",
"args": ["-y", "ss-ge-mcp@1"]
}
}
}
```
<details>
<summary>Из исходников</summary>
```bash
git clone https://github.com/nosuchip/ss-ge-mcp && cd ss-ge-mcp && npm install
claude mcp add ss-ge --scope user -- node "$PWD/src/index.mjs"
npm test # смоук-тест, ходит в сеть по-настоящему
```
</details>
## Инструменты
| Инструмент | Что делает |
|---|---|
| `get_skill` | Инструкции: `apartment-search` -- как разложить критерии; `criteria-coverage` -- что источник знает, а что нет |
| `count` | Счётчики по фильтру, без выкачивания. Один дешёвый запрос |
| `search` | Поиск. `mode="paged"` -- с датами публикации; `mode="fast"` -- быстрее, но без них |
| `listing` | Полная карточка: удобства, координаты, кадастр, просмотры, контакт |
| `geo` | Районы и микрорайоны города с id |
| `cities` | Города и курс USD/GEL |
Фильтр принимает человеческие названия: `subdistricts: ["Ваке", "Сабуртало"]`, и название района раскрывается во все его микрорайоны.
## Что этот сервер намеренно делает неудобно
**Свежесть.** Настоящая дата публикации приходит только в `mode="paged"`. В `mode="fast"` и в `listing` API отдаёт незаполненную дату, поэтому `published` там честно `null` -- это "неизвестно", а не "свежее". Поле `bumped` -- дата платного поднятия, у топовых объявлений почти всегда "сегодня"; признаком свежести не является.
**Счётчики.** Их три, и они не совпадают. Для одного фильтра: `cards` 15 046,
`applications` 50 197, `mapped` 23 337. Это карточки после схлопывания дублей,
сырые объявления и объекты с координатами соответственно. Ни одно не равно
"числу уникальных квартир" -- одно жильё часто висит несколькими объявлениями. Сервер отдаёт все три с пояснением и не выбирает "главное".
**Молчаливые фильтры.** API игнорирует неизвестные поля без ошибки, а ценовой фильтр
без `priceType` -- тоже без ошибки. Опечатка давала бы не ошибку, а полную выдачу
под видом отфильтрованной. Поэтому имена полей валидируются на входе, а `priceType`
подставляется автоматически.
Отдельно: фильтра по району в API нет вообще (`districtIds` сайт кладёт в URL,
но сервер его игнорирует), поэтому район разворачивается в список микрорайонов.
## Ограничения имплементации
Темп 1 запрос/сек глобально, честный `User-Agent` -- сервер не выдаёт себя за браузер
(проверено: ss.ge отдаёт данные и без маскировки). На 403 или 429 размыкается
предохранитель и остаётся разомкнутым: это осознанно, повторять запросы нельзя.
Токен берётся оттуда же, откуда его берёт браузер: ss.ge кладёт готовый анонимный
JWT в `__NEXT_DATA__` каждой страницы. Никаких чужих секретов в конфиге не хранится.
## ⚠️ robots.txt
`api-gateway.ss.ge/robots.txt` содержит `User-agent: * / Disallow: /` -- шлюз запрещает
автоматический доступ целиком. **Этот сервер его не соблюдает.**
Обоснование: robots.txt -- протокол исключения для краулеров, а здесь клиент выполняет
конкретный запрос конкретного пользователя, не обходит и не индексирует сайт. Но это
именно интерпретация, а не разрешение: оператор явно выразил нежелание видеть
автоматический доступ. Соблюдай темп, не выкачивай базу целиком и понимай, что
формальных прав на это у тебя нет.
Публичного контракта у API тоже нет: `buildId` фронта меняется с каждым деплоем,
структура может поехать в любой момент. Для личного поиска -- приемлемо;
как на стабильный источник данных закладываться не стоит.
## Настройки
| Переменная | По умолчанию | Смысл |
|---|---|---|
| `SSGE_LOCALE` | `ru` | `ru`, `en` или `ka` -- переводит **данные**, не только интерфейс |
| `SSGE_MIN_INTERVAL_MS` | `1000` | Минимальный интервал между запросами |
## Лицензия
MIT — см. [`LICENSE`](LICENSE).
TDQS
Scored across 6 tools
Each tool maps to a distinct operation: search returns listings, count returns only counts, listing returns a full detail card, cities/geo are reference data, and get_skill is meta-guidance. The only mild overlap is search and count, which both accept the same filter object, but the descriptions clearly separate the cheap count-only path from a full search.
All names are lowercase single tokens, which is superficially tidy, but grammatical patterns are mixed: get_skill is verb_noun while search/count are verbs and listing/cities/geo are bare nouns. Readable but not a predictable pattern an agent can anticipate.
Six tools is well-scoped for a read-only real-estate search wrapper: query (search/count), detail (listing), reference data (cities/geo), and guidance (get_skill). Each earns its place without redundancy or bloat.
The surface covers the full search workflow — discovery, counting, detail retrieval, and geographic/currency reference data — plus upfront guidance. Since it is read-only by design, create/update/delete do not apply; only minor gaps like sorting or export exist.