Newer
Older
vmk-360_data_mcp / docs / API.md

API Reference — VMK Data MCP Server

Детальное описание JSON-интерфейса каждого MCP-инструмента.


Соглашения

  • Все текстовые запросы (query) должны быть на украинском языке. AI-агент выполняет перевод перед вызовом инструмента.
  • Все ответы сервера — UTF-8 JSON.
  • Ошибки возвращаются как объект {"error": "<сообщение>"}.

Общие типы

PaginationParams

{
  "limit": 20,
  "offset": 0
}
Поле Тип По умолч. Ограничения
limit integer 20 1 ≤ limit ≤ 100
offset integer 0 offset ≥ 0

MetadataFilters

{
  "deal_type": "sale",
  "city": "Київ",
  "district": "Печерський",
  "rooms_count": 2,
  "min_price": 50000,
  "max_price": 150000,
  "currency": "USD",
  "min_total_area": 50,
  "max_total_area": 100,
  "building_type": "monolith",
  "floor": 5,
  "listing_status": "active",
  "metro_station": "Арсенальна"
}

Все поля — опциональные. Указанные фильтры объединяются через AND.

Поле Тип Допустимые значения
deal_type string "sale", "rent_long", "rent_short"
city string Любой (украинский)
district string Любой (украинский)
rooms_count integer ≥ 0
min_price number ≥ 0
max_price number ≥ 0
currency string "USD", "EUR", "UAH"
min_total_area number ≥ 0 (м²)
max_total_area number ≥ 0 (м²)
building_type string "brick", "panel", "monolith", "gas_block", "wood"
floor integer ≥ 0
listing_status string "active", "sold", "rented", "removed", "archived"
metro_station string Любой (украинский)

ListingResult

{
  "id": 12345,
  "title": "2-кімнатна квартира в центрі Києва",
  "description": "...",
  "generated_description": "...",
  "price": 125000,
  "currency": "USD",
  "deal_type": "sale",
  "city": "Київ",
  "district": "Печерський",
  "rooms_count": 2,
  "total_area": 78.5,
  "living_area": 45.0,
  "kitchen_area": 12.0,
  "floor": 5,
  "floors_count": 12,
  "building_type": "monolith",
  "building_year": 2015,
  "renovation_status": "euro_repair",
  "balcony_count": 1,
  "bathroom_type": "combined",
  "parking_type": "underground",
  "heating_type": "central",
  "layout_type": "standard",
  "window_view": "courtyard",
  "metro_station": "Арсенальна",
  "metro_distance_type": "walking",
  "metro_distance_meters": 450,
  "url_source": "https://...",
  "publish_date": "2024-03-15",
  "images_count": 18,
  "contact_phone": "+380...",
  "listing_status": "active",
  "archived_at": null,
  "created_at": "2024-03-15",
  "updated_at": "2024-03-20",
  "similarity_score": 0.842,
  "rank_score": 0.123
}
Поле Тип Примечание
similarity_score `number \ null` Только для search_similar_listings (cosine similarity ≈ 1 − distance/2)
rank_score `number \ null` Только для search_by_metadata (ts_rank_cd)

SearchResult

{
  "total": 137,
  "limit": 20,
  "offset": 0,
  "listings": [ /* массив ListingResult */ ]
}
Поле Тип Описание
total integer Общее количество записей, соответствующих фильтру (без LIMIT)
limit integer Фактический limit
offset integer Фактический offset
listings ListingResult[] Список объявлений

Инструменты

search_similar_listings

Векторный поиск по смыслу запроса. Использует pgvector + HNSW индекс.

MCP-запрос (пример):

{
  "name": "search_similar_listings",
  "arguments": {
    "query": "сучасна 2-кімнатна квартира біля метро з ремонтом",
    "filters": {
      "city": "Київ",
      "deal_type": "sale",
      "min_price": 100000,
      "max_price": 200000,
      "currency": "USD"
    },
    "pagination": {
      "limit": 10,
      "offset": 0
    },
    "min_similarity": 0.75
  }
}

Логика:

  1. Запрос отправляется в Ollama → получаем embedding vector(768).
  2. SQL:
    SELECT <USER_COLUMNS>,
           1 - (embedding <=> $1::vector) / 2.0 AS similarity_score
    FROM property_listings
    WHERE city = $2 AND deal_type = $3 AND price BETWEEN $4 AND $5
      AND embedding <=> $1::vector <= $6
    ORDER BY embedding <=> $1::vector
    LIMIT $7 OFFSET $8;
  3. Дополнительный COUNT(*)-запрос для поля total.

Ошибки:

  • Сервис эмбеддингов недоступен — Ollama недоступна
  • Ошибка при поиске — проблема с БД или валидацией
  • Неожиданная ошибка — внутренняя ошибка сервера

search_by_metadata

Полнотекстовый поиск через search_vector (украинский FTS).

MCP-запрос (пример):

{
  "name": "search_by_metadata",
  "arguments": {
    "query": "квартира Печерський район метро",
    "filters": {
      "listing_status": "active",
      "rooms_count": 3
    },
    "pagination": {
      "limit": 20,
      "offset": 0
    }
  }
}

Логика:

  1. Запрос обрабатывается plainto_tsquery('ukrainian', $1).
  2. SQL:
    SELECT <USER_COLUMNS>,
           ts_rank_cd(search_vector, query) AS rank_score
    FROM property_listings,
         plainto_tsquery('ukrainian', $1) query
    WHERE search_vector @@ query
      AND listing_status = $2 AND rooms_count = $3
    ORDER BY rank_score DESC
    LIMIT $4 OFFSET $5;
  3. Дополнительный COUNT(*).

Ошибки: те же, что и для search_similar_listings, но без ошибок Ollama.


get_listing_by_id

Получение одного объявления по id.

MCP-запрос (пример):

{
  "name": "get_listing_by_id",
  "arguments": {
    "listing_id": 12345
  }
}

Логика:

SELECT <USER_COLUMNS>
FROM property_listings
WHERE id = $1;

Ошибки:

  • {"error": "Объявление не найдено"} — если id отсутствует

describe_schema

Описание схемы таблицы для подсказок AI-агенту.

MCP-запрос (пример):

{
  "name": "describe_schema",
  "arguments": {}
}

Возвращает JSON-объект со списком колонок, их типами, индексами и ограничениями, которые агент может использовать для формирования запросов.


Коды ошибок HTTP транспорта

При работе через Streamable HTTP:

Статус Причина
200 Успешный SSE-стрим
202 Успешный POST (accept message)
400 Невалидный JSON или параметры
404 Endpoint не найден
405 Неподдерживаемый HTTP-метод
500 Внутренняя ошибка сервера

Примеры сценариев

Сценарий 1: "Найди 3-комнатную квартиру в Киеве для покупки"

  1. Агент переводит на украинский: "3-кімнатна квартира Київ продаж".
  2. Агент выбирает векторный поиск для семантической близости.
  3. MCP-вызов:
    {
      "name": "search_similar_listings",
      "arguments": {
        "query": "3-кімнатна квартира Київ продаж",
        "filters": { "city": "Київ", "deal_type": "sale", "rooms_count": 3 },
        "pagination": { "limit": 10, "offset": 0 },
        "min_similarity": 0.7
      }
    }

Сценарий 2: "Есть ли квартиры у метро Арсенальная?"

  1. Перевод: "квартира біля метро Арсенальна".
  2. FTS-поиск для точного ключевого слова "Арсенальна":
    {
      "name": "search_by_metadata",
      "arguments": {
        "query": "квартира біля метро Арсенальна",
        "filters": { "metro_station": "Арсенальна", "listing_status": "active" },
        "pagination": { "limit": 20, "offset": 0 }
      }
    }

Сценарий 3: "Покажи объявление № 12345"

{
  "name": "get_listing_by_id",
  "arguments": { "listing_id": 12345 }
}