livo-ge-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex Xela
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # livo-ge-mcp
2
+
3
+ MCP-сервер над [livo.ge](https://livo.ge) - порталом недвижимости группы tnet
4
+ (бывший myhome.ge).
5
+
6
+ Прозрачный stateless-прокси: один вызов инструмента = один-несколько живых запросов.
7
+ Между вызовами кешируются только два справочника (локации и словари, TTL 6 часов);
8
+ объявления не кешируются никогда.
9
+
10
+ У livo.ge нет публичного API. Контракт восстановлен реверс-инжинирингом бандлов
11
+ Next.js и проверен живыми запросами - разбор в [`docs/API.md`](docs/API.md).
12
+
13
+ ## Установка
14
+
15
+ ```bash
16
+ claude mcp add livo-ge --scope user -- npx -y livo-ge-mcp@1
17
+ ```
18
+
19
+ Нужен Node >= 20. Клонировать и ставить вручную ничего не надо: `npx` сам скачает пакет
20
+ из npm и закеширует. `@1` фиксирует мажорную версию, обновления внутри неё приезжают
21
+ сами, а ломающий релиз молча не подменит сервер.
22
+
23
+ Проверить, что сервер подключился: `claude mcp list`.
24
+
25
+ Для клиентов с JSON-конфигом (Claude Desktop, Cursor, Windsurf):
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "livo-ge": {
31
+ "command": "npx",
32
+ "args": ["-y", "livo-ge-mcp@1"],
33
+ "env": { "LIVO_LOCALE": "ru" }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ <details>
40
+ <summary>Из исходников</summary>
41
+
42
+ ```bash
43
+ git clone https://github.com/nosuchip/livo-ge-mcp && cd livo-ge-mcp && npm install
44
+ claude mcp add livo-ge --scope user -- node "$PWD/src/index.mjs"
45
+ npm test # смоук-тест, ходит в сеть по-настоящему
46
+ ```
47
+
48
+ </details>
49
+
50
+ ### Настройки
51
+
52
+ | Переменная | По умолчанию | Что делает |
53
+ |---|---|---|
54
+ | `LIVO_LOCALE` | `ru` | `ka` \| `en` \| `ru`. Заголовок переводит **данные**, а не только UI |
55
+ | `LIVO_MIN_INTERVAL_MS` | `1000` | Минимальный интервал между запросами, глобально |
56
+
57
+ Задать при установке: `claude mcp add livo-ge --scope user -e LIVO_LOCALE=en -- npx -y livo-ge-mcp@1`.
58
+
59
+ ## Инструменты
60
+
61
+ | Инструмент | Что делает |
62
+ |---|---|
63
+ | `get_skill` | Инструкции: `apartment-search` - как разложить критерии; `criteria-coverage` - что источник знает, а что нет |
64
+ | `count` | Число объявлений под фильтр, без выкачивания. Один дешёвый запрос |
65
+ | `search` | Поиск. Отдаёт `applied_filters` - эхо API о том, что он реально разобрал |
66
+ | `listing` | Полная карточка: описание, удобства, координаты, кадастр, просмотры, настоящая дата публикации |
67
+ | `geo` | Города, районы, микрорайоны с id; `query` работает как автодополнение сайта |
68
+ | `reference` | Словари id → название для фильтров, которые принимают только id |
69
+ | `projects` | Новостройки (проекты застройщиков) - отдельный набор данных |
70
+
71
+ Фильтр принимает человеческие названия на любом из трёх языков сайта:
72
+ `areas: ["Ваке", "Saburtalo"]`, `metro: ["Руставели"]`,
73
+ `amenities: ["elevator", "pets-allowed"]`.
74
+
75
+ ## Что этот сервер намеренно делает неудобно
76
+
77
+ Пять вещей в livo устроены так, что наивная обёртка выдавала бы правдоподобную неправду.
78
+ Здесь они вынесены наружу, а не спрятаны.
79
+
80
+ **Свежесть.** `last_updated` - не дата публикации: livo пишет туда любую правку и любое
81
+ платное поднятие, и у объявления 2024 года там регулярно "сегодня". Настоящий возраст
82
+ даёт `quantity_of_day`, поэтому сервер отдаёт `age_days` и посчитанный из него
83
+ `published`, а `updated` подписан как "правка или поднятие". Проверено сверкой
84
+ с `created_at`: сходится день в день.
85
+
86
+ **Сортировка.** Она не применяется ко всей выдаче: `super_vip`, `vip_plus` и `vip`
87
+ закреплены сверху всегда, а `order` работает только внутри тира. Сам `date_desc`
88
+ к тому же сортирует по `last_updated`, а не по публикации. Поэтому каждая карточка
89
+ несёт `promo_tier` (`null` = обычное объявление), и в ответе прямо сказано, что
90
+ позиция в списке про деньги, а не про свежесть.
91
+
92
+ **Опечатки в фильтре.** API молча игнорирует неизвестные имена полей и отдаёт
93
+ неотфильтрованную выдачу: `count` с полем `nonsense_field` возвращает ровно столько же,
94
+ сколько без него. Клиент сверяет фильтр с белым списком и падает, а не врёт.
95
+ Плюс к этому в ответ кладётся `applied_filters` - эхо самого API.
96
+
97
+ **Удобства объединяются по ИЛИ.** `[elevator, conditioner]` даёт *больше* результатов,
98
+ чем `[elevator]`. "С лифтом И кондиционером" через этот API одним запросом не
99
+ выражается, поэтому сервер предупреждает об этом прямо в ответе.
100
+
101
+ **Охват.** Заголовок `X-Website-Key` выбирает витрину группы tnet, и витрины не равны:
102
+ по одному запросу livo отдаёт 53 698 объявлений, myhome.ge - 94 116. Livo - подмножество,
103
+ и `criteria-coverage` говорит об этом прямо.
104
+
105
+ ## Темп и вежливость
106
+
107
+ 1 запрос в секунду глобально, честный User-Agent (`livo-ge-mcp/…`), без подделки под
108
+ браузер. На 429 и на "настоящий" 403 предохранитель размыкается и остаётся разомкнутым:
109
+ это сигнал перестать ходить. `robots.txt` на обоих API-хостах ничего не запрещает.
110
+
111
+ ## Лицензия
112
+
113
+ MIT, см. [`LICENSE`](LICENSE).
package/docs/API.md ADDED
@@ -0,0 +1,261 @@
1
+ # livo.ge - реверс-инжиниринг API (для оформления в MCP)
2
+
3
+ Разобрано 2026-09-06. Всё ниже проверено живыми запросами, не выведено из кода.
4
+ Публичного/документированного API нет. Схема восстановлена из бандлов Next.js
5
+ (`livo.ge/_next/static/chunks/*.js`) плюс эмпирическая проверка каждого фильтра
6
+ счётчиком.
7
+
8
+ ## 1. Как всё устроено
9
+
10
+ | | |
11
+ |---|---|
12
+ | Фронт | Next.js App Router, страница выдачи - `/s`, карточка - `/udzravi-qoneba/...` |
13
+ | API объявлений | `https://api-statements.tnet.ge` |
14
+ | API локаций | `https://api-locations.tnet.ge` |
15
+ | Базовый путь | `/v1/...` для объявлений, `/v2/...` для локаций, `/api/tnet-projects/...` для новостроек |
16
+ | Авторизация | **нет**. Ни токена, ни куки |
17
+ | Обязательный заголовок | `X-Website-Key: livo` - без него HTTP 403 `{"errors":{"message":["X-Website-Key is required"]}}` |
18
+ | Локаль | заголовок `locale: ka \| en \| ru` - переводит **данные** (город, район, состояние, заголовок), не только UI |
19
+ | robots.txt | `livo.ge` запрещает только `/admin/login.php`; на обоих API-хостах `Disallow:` пуст |
20
+
21
+ Livo - витрина группы tnet (myhome.ge, myauto.ge, mymarket.ge …). Данные лежат
22
+ на общем бэкенде; принадлежность к витрине задаёт `X-Website-Key`.
23
+
24
+ Клиент найден в чанке как axios-инстанс:
25
+
26
+ ```js
27
+ axios.create({
28
+ baseURL: "https://api-statements.tnet.ge/",
29
+ headers: { "X-Website-Key": "livo" },
30
+ paramsSerializer: e => qs.stringify(e, { arrayFormat: "comma",
31
+ filter: (k, v) => typeof v === "boolean" ? +v : (v != null && v !== "" ? v : undefined) }),
32
+ });
33
+ // setLanguageHeader -> http.defaults.headers.locale = e
34
+ ```
35
+
36
+ Отсюда правила сериализации: **массивы через запятую** (`urbans=38,47`),
37
+ **booleans как 1/0**, пустые строки и null не отправляются вовсе.
38
+
39
+ ### X-Website-Key выбирает витрину, и витрины не равны
40
+
41
+ Один и тот же запрос `count?cities=1&deal_types=1`:
42
+
43
+ | ключ | total |
44
+ |---|---|
45
+ | `livo` | 53 698 |
46
+ | `myhome` | 94 116 |
47
+ | любой неизвестный | 53 698 (падает в дефолт = livo) |
48
+
49
+ **Livo показывает подмножество инвентаря tnet.** Это устройство портала, и об этом
50
+ надо говорить пользователю, если он считает livo полным охватом рынка Грузии.
51
+
52
+ ## 2. Эндпоинты
53
+
54
+ | Метод | Путь | Что отдаёт |
55
+ |---|---|---|
56
+ | GET | `/v1/statements` | страница выдачи. **Без общего количества** |
57
+ | GET | `/v1/statements/count` | пагинация по тому же фильтру: `total`, `last_page`, … |
58
+ | GET | `/v1/statements/{id}` | полная карточка, в т.ч. `created_at` |
59
+ | GET | `/v1/statements/similars/{id}` | похожие (подбор livo, критерии не публикуются) |
60
+ | GET | `/v1/statements/statement-parameters` | все справочники одним ответом, **4.5 МБ** |
61
+ | GET | `/api/tnet-projects/listing` | новостройки (проекты застройщиков) |
62
+ | GET | `/api/tnet-projects/{uuid}` | карточка проекта |
63
+ | GET | `{locations}/v2/cities` | город → район → микрорайон → улицы, **4.1 МБ** |
64
+ | GET | `{locations}/v2/suggestions?q=` | автодополнение, отдаёт готовую связку id |
65
+ | GET | `{locations}/v2/streets/grouped` | улицы по `cities`/`districts`/`urbans` |
66
+ | POST | `/v1/statements/phone/show` | полный телефон (в выдаче он замаскирован) |
67
+
68
+ Ответ всегда обёрнут: `{"result": true, "data": …}`. При прикладной ошибке приходит
69
+ HTTP 200 с `result:false` и `errors`.
70
+
71
+ ## 3. Параметры фильтра
72
+
73
+ Полный список снят с nuqs-парсера URL страницы `/s` - имена параметров URL и API
74
+ совпадают один в один.
75
+
76
+ ```
77
+ q page per_page
78
+ deal_types real_estate_types rent_types daily_rent_types
79
+ cities districts urbans streets metro_station_ids
80
+ currency_id price_from price_to price_types square_price_from square_price_to
81
+ area_types area_from area_to
82
+ floor_from floor_to height_from height_to
83
+ statuses conditions project_types
84
+ room_types bedroom_types bathroom_types living_room_types
85
+ storeroom_types parking_types heating_types hot_water_types
86
+ material_types door_window_types
87
+ parameters owner_type users
88
+ has_balcony has_loggia has_porch loggia_area
89
+ with_3d has_cadastral_code can_exchanged is_super_vip
90
+ order_by sequence sorting
91
+ ```
92
+
93
+ Каждый из них проверен счётчиком: `count(cities=1, deal_types=1, real_estate_types=1)`
94
+ = 43 941, и добавление любого фильтра это число меняет.
95
+
96
+ ### Перечисления
97
+
98
+ ```
99
+ deal_types 1 sale, 2 rent, 3 lease, 7 daily, 10 will_be_leased (живых нет)
100
+ real_estate_types 1 flat, 2 house, 3 cottage, 4 land, 5 commercial, 6 hotel
101
+ statuses 1 old building, 2 new building, 3 under construction
102
+ (+ 4,5,6,7,18,30 - категории земли и коммерции)
103
+ currency_id 1 GEL, 2 USD (EUR в фильтре НЕТ)
104
+ area_types 1 m², 2 га
105
+ price_types 1 полная цена, 2 цена за м²
106
+ owner_type physical | broker | agency | developer (строки, не id)
107
+ order_by price | date sequence asc | desc
108
+ sorting 1 price↑, 2 price↓, 3 date↑, 4 date↓ (дублирует пару выше)
109
+ ```
110
+
111
+ Остальные словари (`conditions`, `project_types`, `heating_types`, `material_types`,
112
+ `parameters` …) - только id, берутся из `/v1/statements/statement-parameters`.
113
+ Часть словарей там - объект, ключ = `real_estate_type_id`.
114
+
115
+ У удобств (`parameters`) есть устойчивый английский ключ `svg_file_name`
116
+ (`elevator`, `conditioner`, `guard`, `swimming-pool-open`, `pets-allowed` …) -
117
+ он не меняется от `locale` и годится как канонический идентификатор.
118
+
119
+ ## 4. Грабли
120
+
121
+ ### 4.1. Неизвестные параметры игнорируются молча
122
+
123
+ ```
124
+ count(cities=1, deal_types=1, real_estate_types=1) -> 43941
125
+ count(cities=1, deal_types=1, real_estate_types=1, nonsense_field=42) -> 43941
126
+ ```
127
+
128
+ Опечатка в имени поля не даёт ни ошибки, ни предупреждения - просто выдача
129
+ оказывается неотфильтрованной. Поэтому клиент сверяет фильтр с белым списком
130
+ и падает сам.
131
+
132
+ ### 4.2. Выдача не знает своего размера
133
+
134
+ `/v1/statements` не содержит ни `total`, ни `last_page` - только массив.
135
+ Количество даёт **отдельный** запрос `/v1/statements/count` с тем же фильтром.
136
+
137
+ ### 4.3. `seo.filters` - эхо разбора
138
+
139
+ И `/v1/statements`, и `/api/tnet-projects/listing` возвращают `seo.filters` -
140
+ то, что API реально разобрал из query:
141
+
142
+ ```json
143
+ "seo": {"filters": {"cities": [1], "urbans": [38], "deal_types": [1],
144
+ "real_estate_types": [1], "room_types": [2,3], "districts": [], …}}
145
+ ```
146
+
147
+ Полезно как канал проверки: если фильтровал по району, а в эхе пусто - не применилось.
148
+ Эхо покрывает только локацию, типы и комнаты; цену, площадь и удобства оно не отражает.
149
+
150
+ ### 4.4. `last_updated` - не дата публикации
151
+
152
+ Главная ловушка источника.
153
+
154
+ | id | `created_at` | `quantity_of_day` | `last_updated` |
155
+ |---|---|---|---|
156
+ | 18826955 | 2024-07-22 | 776 | **2026-09-06** |
157
+ | 24616406 | 2026-05-02 | 127 | **2026-09-06** |
158
+ | 25915509 | 2026-09-01 | 5 | 2026-09-01 |
159
+
160
+ `last_updated` меняется от любой правки и от платного поднятия, поэтому у объявления
161
+ двухлетней давности он сплошь и рядом "сегодня".
162
+
163
+ Настоящий возраст даёт **`quantity_of_day`** - разница в календарных днях между
164
+ сегодня и датой публикации. Проверено на выборке: сходится с `created_at` из карточки
165
+ объявления день в день. Дата публикации = `сегодня - quantity_of_day`.
166
+
167
+ `created_at` приходит только в `/v1/statements/{id}`; в выдаче его нет.
168
+
169
+ ### 4.5. Сортировка не применяется ко всей выдаче
170
+
171
+ Платные тиры закреплены **над** сортировкой, и это не обходится параметрами.
172
+ Батуми, `order_by=date`, первые 12 карточек:
173
+
174
+ | # | sequence=asc | sequence=desc |
175
+ |---|---|---|
176
+ | 1 | `super_vip`, upd 09-04 | `super_vip`, upd 09-04 |
177
+ | 2-3 | `vip_plus`, upd 09-05 / 09-06 | `vip_plus`, upd 09-06 / 09-05 |
178
+ | 4-7 | `vip`, upd 09-05…09-06 | `vip`, upd 09-06…09-05 |
179
+ | 8+ | обычные, upd 2025-09-18 → … | обычные, upd 09-06 12:28 → … |
180
+
181
+ Порядок тиров: `is_super_vip` → `is_vip_plus` → `is_vip` → обычные, и только внутри
182
+ каждого тира работает заданная сортировка. Первые 5-10 позиций почти любой страницы
183
+ куплены.
184
+
185
+ **Кроме того, `order_by=date` сортирует по `last_updated`, а не по дате публикации.**
186
+ Объявление 2023 года, поднятое сегодня, встанет выше вчерашнего: в тесте по Ваке
187
+ `sequence=desc` дал подряд `age_days` = 0, **4**, 0, 0, 0 - вторая позиция это
188
+ `vip_plus` четырёхдневной давности, поднятый только что.
189
+
190
+ Единственный надёжный признак свежести - `quantity_of_day`.
191
+
192
+ ### 4.6. Списочные фильтры - ИЛИ, и для удобств это ловушка
193
+
194
+ ```
195
+ count(deal_types=2, real_estate_types=1, cities=1, urbans=47) -> 35681
196
+ count(…, parameters=6) # лифт -> 13832
197
+ count(…, parameters=6,4) # лифт + кондиционер -> 18806
198
+ ```
199
+
200
+ Больше, а не меньше. Для `room_types` такое поведение ожидаемо, а для `parameters`
201
+ нет: "с лифтом И кондиционером" через этот API одним запросом не выражается.
202
+
203
+ ### 4.7. `room_types` - словарь, а не количество комнат
204
+
205
+ ```
206
+ id 1 2 3 4 5 7 8 9 10 11
207
+ шт 1 2 3 4 5 6 7 8 9 10+
208
+ ```
209
+
210
+ id 6 отсутствует вовсе, дальше пятёрки id и число комнат расходятся. Наивное
211
+ `room_types=6` отфильтрует семикомнатные. `bedroom_types`, наоборот, 1:1
212
+ с количеством спален (1…10, где 10 = "10+"). В карточке выдачи поля `room`
213
+ и `bedroom` - уже человеческие значения, а в `/v1/statements/{id}` лежат
214
+ `room_type_id` / `bedroom_type_id`, то есть id словаря.
215
+
216
+ ### 4.8. Цена
217
+
218
+ `price_from`/`price_to` трактуются в валюте `currency_id`, **по умолчанию GEL**.
219
+ Цена за метр - это отдельная пара `square_price_from`/`square_price_to`, а не
220
+ `price_types` при той же паре. В ответе цена приходит сразу в трёх валютах:
221
+
222
+ ```json
223
+ "price": {"1": {...GEL}, "2": {...USD}, "3": {...EUR}}
224
+ ```
225
+
226
+ EUR есть только в выдаче; фильтровать по нему нельзя.
227
+
228
+ ### 4.9. Локации: микрорайоны есть практически только у Тбилиси
229
+
230
+ 126 городов, 3041 район, **76 микрорайонов** - из них 75 в Тбилиси. За пределами
231
+ Тбилиси "районы" - это сёла и посёлки муниципалитета, и фильтровать надо через
232
+ `districts`, а не `urbans`. id районов и микрорайонов глобально уникальны, так что
233
+ город можно не передавать (но лучше передавать).
234
+
235
+ `/v2/cities` весит 4.1 МБ, потому что тащит в себе все сгруппированные улицы.
236
+ Группа `cities` внутри `statement-parameters` - ещё 4.6 МБ того же самого.
237
+
238
+ ### 4.10. Прочее
239
+
240
+ * `per_page` не ограничен сверху в разумных пределах: 200 отдаёт 200 объектов.
241
+ * Слаг в URL карточки косметический - `https://livo.ge/udzravi-qoneba/x/y-25962557`
242
+ отдаёт 200. Значение имеет только id. Формат:
243
+ `/{locale?}/udzravi-qoneba/{middle_slug}/{href_lang[locale]}-{id}`,
244
+ где `middle_slug` = `{iyideba|qiravdeba|giravdeba|qiravdeba-dghiurad}-{bina|kerdzo-saxli|agaraki|miwis-nakveti|komertsiuli-farti|sastumro}`.
245
+ * Телефон в выдаче замаскирован (`568963***`); полный отдаёт POST `/v1/statements/phone/show`.
246
+ Этот эндпоинт в MCP намеренно не заворачивается.
247
+ * У новостроек (`/api/tnet-projects/listing`) **нет никаких дат** - ни публикации,
248
+ ни обновления цен. Свежесть цен по этому источнику оценить нельзя. Имена проектов
249
+ и застройщиков приходят по-грузински независимо от `locale`.
250
+ * `has_cadastral_code=1` почти ничего не отсекает (43 939 из 43 941) - поле есть
251
+ почти у всех, фильтр практически бесполезен.
252
+ * `with_3d=1` по Тбилиси даёт единицы объявлений.
253
+
254
+ ## 5. Темп и вежливость
255
+
256
+ Авторизации нет, лимитов в ответах не видно, `Retry-After` не встречался.
257
+ Обёртка держит 1 запрос/секунду глобально, ходит с честным User-Agent
258
+ (`livo-ge-mcp/1.0.0 (+…)`) и не притворяется браузером - проверено, что API
259
+ отдаёт данные и без подделки UA. На 403 (кроме "X-Website-Key is required",
260
+ это ошибка конфигурации) и на 429 предохранитель размыкается и остаётся
261
+ разомкнутым: ретраить блокировку - худшее, что можно сделать.
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "livo-ge-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP-сервер над livo.ge - порталом недвижимости группы tnet. Прозрачный stateless-прокси.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Alex Xela",
8
+ "homepage": "https://github.com/nosuchip/livo-ge-mcp#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/nosuchip/livo-ge-mcp.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/nosuchip/livo-ge-mcp/issues"
15
+ },
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "bin": {
20
+ "livo-ge-mcp": "src/index.mjs"
21
+ },
22
+ "main": "src/index.mjs",
23
+ "files": [
24
+ "src",
25
+ "docs",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "scripts": {
30
+ "start": "node src/index.mjs",
31
+ "test": "node test/smoke.mjs"
32
+ },
33
+ "keywords": [
34
+ "mcp",
35
+ "model-context-protocol",
36
+ "real-estate",
37
+ "georgia",
38
+ "tbilisi",
39
+ "livo.ge",
40
+ "myhome.ge"
41
+ ],
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.30.0",
44
+ "zod": "^3.25.0"
45
+ }
46
+ }