Skip to main content
Glama

hh-radar

ci python license

Своя база вакансий hh.ru, которую LLM-агент опрашивает как набор инструментов. Полнотекстовый поиск на PostgreSQL, поиск по смыслу на pgvector, семь MCP-инструментов и честный замер того, где поиск ошибается.

📊 Витрина — открывается за десять секунд, все цифры посчитаны программой из базы. Сейчас на ней демонстрационный корпус: вакансии hh нельзя ни выложить в публичный репозиторий, ни скачать без токена приложения, поэтому страница собрана из сгенерированных данных и говорит об этом первым же абзацем.


Зачем это написано

Я искал работу через hh.ru и делал руками одно и то же: открыть выдачу, прочитать тридцать описаний, выписать, какие навыки повторяются, прикинуть, куда вообще есть смысл писать. Поиск на hh отвечает на «есть ли слово в тексте». На вопросы, которые возникали у меня — «где чаще всего просят n8n и сколько за это платят», «какие вакансии возьмут человека без коммерческого опыта», «где нужно чинить чужие сломанные автоматизации» — он не отвечает вообще.

Поэтому вакансии складываются в свою базу, а поверх базы стоит MCP-сервер. Дальше вопрос задаётся обычными словами прямо в Claude Desktop, а агент сам решает, чем его обслужить: полнотекстовым поиском, агрегацией по навыкам, срезом зарплат или поиском по смыслу.

Ты: какие навыки чаще всего требуют в вакансиях по автоматизации за последний месяц?

Claude вызывает skill_stats(query="автоматизация", published_within_days=30)
→ Python — 61% вакансий, медиана 140 000 ₽
  n8n — 34%, медиана 150 000 ₽
  Docker — 29%, ...

Цифры в примере — иллюстрация формата ответа. Настоящие лежат на витрине и меняются с каждым сбором.

Related MCP server: HuntFlow MCP Server

Что внутри

Слой

Что делает

hh/

Клиент API hh.ru: OAuth-токен приложения, ограничение частоты, ретраи, обход потолка выдачи

ingest/

Идемпотентная запись в базу: UPSERT, двухфазная загрузка карточек

db/

Четыре сущности плюс связь многие-ко-многим, полнотекстовый индекс, слой запросов

rag/

Чанкинг, эмбеддинги, поиск по смыслу на pgvector, гибридный поиск, оценка качества

mcp_server/

Семь инструментов для LLM-агента поверх всего перечисленного

showcase/

Генератор статической витрины для GitHub Pages

Инструменты MCP

Инструмент

Отвечает на вопрос

search_vacancies

«Найди вакансии со словами X, удалённо, от 150 тысяч»

semantic_search

«Найди вакансии, где надо чинить чужие сломанные автоматизации»

get_vacancy

«Покажи эту вакансию целиком»

skill_stats

«Какие навыки требуют чаще всего и сколько за них платят»

market_overview

«Какая вилка на рынке, сколько удалёнки, кто больше всех нанимает»

compare_to_profile

«Мне сюда стоит писать?» — с честным ответом «нет, и вот почему»

db_status

«Что вообще есть в базе и за какой период» — чтобы агент не выдумывал

Быстрый старт

Нужны Docker и Python 3.12+.

git clone https://github.com/Denmurzik/hh-radar
cd hh-radar

uv sync --extra rag          # или: pip install -e ".[rag]"
docker compose up -d db      # PostgreSQL 17 + pgvector на порту 5433
uv run alembic upgrade head  # схема
uv run hh-radar seed         # 52 демонстрационные вакансии, без токена и без сети
uv run hh-radar status

Через минуту у вас работающая база и MCP-сервер. Дальше — либо подключить его к Claude Desktop (см. ниже), либо собрать настоящие данные.

Вакансии, которые кладёт seed, вымышлены: сорок восемь из них собирает scripts/make_demo_corpus.py из двенадцати ролевых архетипов с фиксированным зерном. Чужие объявления в публичном репозитории лежать не должны, а без токена приложения их и не получить. Проходят они через тот же разбор, тот же конвейер записи и тот же чанкинг, что и живые данные, — отдельной ветки «для демо» в коде нет. Подробности в samples/README.md.

Настоящие данные

GET /vacancies у hh закрыт для анонимных запросов — отвечает 403 bad_authorization. Нужен токен приложения:

  1. Зайдите на dev.hh.ru/admin → «Создать приложение».

  2. Скопируйте Client ID и Client Secret.

  3. cp .env.example .env и впишите их в HH_CLIENT_ID / HH_CLIENT_SECRET.

Токен по client_credentials запрашивается автоматически и кэшируется на диск — /token не дёргается на каждый запуск.

uv run hh-radar ingest --days 30      # собрать вакансии
uv run hh-radar index                 # посчитать эмбеддинги
uv run hh-radar showcase              # пересобрать витрину

Подключение к Claude Desktop

%APPDATA%\Claude\claude_desktop_config.json (Windows) или ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):

{
  "mcpServers": {
    "hh-radar": {
      "command": "C:\\путь\\к\\hh-radar\\.venv\\Scripts\\python.exe",
      "args": ["-m", "hh_radar.mcp_server"],
      "env": { "DATABASE_URL": "postgresql+psycopg://hh:hh@localhost:5433/hh_radar" }
    }
  }
}

Перезапустите Claude Desktop — в списке инструментов появится hh-radar.

Схема базы

erDiagram
    EMPLOYERS ||--o{ VACANCIES : "нанимает"
    VACANCIES ||--o{ VACANCY_CHUNKS : "режется на"
    VACANCIES }o--o{ SKILLS : "требует"

    EMPLOYERS {
        bigint id PK "идентификатор hh"
        string name
        bool trusted
    }
    VACANCIES {
        bigint id PK "идентификатор hh"
        string name
        int salary_from_rub "приведено к рублям"
        bool is_remote
        text description
        tsvector search_vector "генерируемый, GIN"
        timestamptz published_at "индекс DESC"
    }
    SKILLS {
        int id PK
        string name UK "нормализованное"
        string display_name "как написал работодатель"
    }
    VACANCY_CHUNKS {
        bigint id PK
        int chunk_index
        text content
        vector embedding "384, HNSW cosine"
    }

Инженерные решения, которые стоит объяснить

Здесь то, что не видно из списка технологий, но именно это отличает работающий сборщик от примера из документации.

Потолок выдачи в 2000 элементов. hh не отдаёт больше двух тысяч вакансий на один поисковый запрос, сколько страниц ни проси. Сборщик сначала спрашивает, сколько всего найдено за интервал дат; если больше потолка — режет интервал пополам и повторяет для каждой половины рекурсивно. Дробление останавливается на часовом окне: если и там перебор, честнее записать предупреждение в лог, чем молча потерять данные.

tsvector — генерируемый столбец, а не вычисление на лету. Postgres поддерживает его сам при каждой записи, GIN-индекс строится по готовому значению. Название вакансии весит больше описания (setweight A против B) — совпадение в заголовке релевантнее совпадения где-то в середине абзаца про ДМС.

Проверить план: hh-radar explain "kubernetes airflow". Команда не просто печатает EXPLAIN ANALYZE, а разбирает его, потому что «Seq Scan» сам по себе ещё ничего не означает. На маленькой таблице последовательное чтение дешевле индекса, и планировщик прав. На большой, но при запросе, под который подходит половина базы, — тоже прав. Проблема только в третьем случае: запрос избирательный, таблица большая, а индекс не взят — обычно потому, что после массовой заливки не собрана статистика. Поэтому hh-radar ingest в конце сам выполняет ANALYZE: без этого первые же поиски по свежей базе идут мимо индекса.

Различение INSERT и UPDATE без лишнего SELECT. У INSERT ... ON CONFLICT DO UPDATE системный столбец xmax равен нулю у только что вставленной строки и содержит идентификатор транзакции у обновлённой. RETURNING (xmax = 0) даёт точную статистику прогона одним запросом.

Двухфазная загрузка. Поиск отдаёт сто вакансий за запрос, но без описания и навыков; полная карточка стоит одного запроса на вакансию. Поэтому сначала пишется всё, что дал поиск, и только потом дозагружаются карточки — по тем вакансиям, у которых их ещё нет. Прерванный на середине прогон ничего не теряет, следующий подхватывает с того же места.

Название вакансии в каждом чанке. Кусок текста «требуется опыт от года и знание Docker» не находится по запросу «python-разработчик»: слова «python» в самом куске нет. Поэтому каждый чанк начинается с названия вакансии. Мелочь, которая меняет recall в разы.

Схлопывание по вакансии в семантическом поиске. Без этого одна многословная вакансия занимает всю первую страницу выдачи своими пятью чанками. Берём с запасом, оставляем лучший чанк каждой вакансии.

RRF, а не взвешенная сумма. Гибридный поиск объединяет полнотекстовую и семантическую выдачу по Reciprocal Rank Fusion. Складывать ts_rank с косинусной близостью нельзя: это числа из разных пространств, и коэффициент для их смешивания подбирался бы под конкретный набор запросов. RRF работает с рангами, а ранги сравнимы всегда.

Заглушка вместо модели в CI. EMBEDDING_BACKEND=hash — детерминированный хеш-эмбеддер той же размерности. Он ничего не знает о смысле, но позволяет прогнать всю проводку кода, не выкачивая 220 МБ ONNX на каждый пуш. Настоящая модель — paraphrase-multilingual-MiniLM-L12-v2 через fastembed, на CPU, без API-ключей: проверяющий должен уметь запустить проект, не заводя аккаунтов.

Где поиск ошибается

Чисел здесь нет, и это осознанно. Опубликовать recall@10 по вымышленному корпусу, который сам же и сгенерировал, — значит принять экзамен у самого себя: запросы и данные писал один человек, и метрика померит их совпадение, а не качество поиска. Настоящие числа появятся здесь после сбора с токеном приложения; до тех пор честнее пустая таблица, чем красивая.

Что при этом сделано и работает:

uv run hh-radar evaluate                      # в терминал
uv run hh-radar evaluate -o eval/report.md    # в файл

Команда гоняет размеченный набор из 25 запросов через три метода поиска — полнотекстовый, семантический и гибридный — и считает recall@k, precision@k, MRR и среднее время ответа для каждого. Эталон берётся из relevant_vacancy_ids, а пока ручной разметки нет — из регулярных выражений по названию вакансии. Запросы, для которых эталона не нашлось вообще, не идут в среднее и считаются отдельной колонкой: записать их в провалы было бы враньём в свою пользу наоборот.

Интереснее таблицы — то, что команда печатает под ней: список запросов, на которых метод не нашёл ни одной релевантной вакансии в топ-5, вместе с тем, что он вернул вместо неё. По нему видно класс запросов, который система не тянет. Даже на демонстрационном корпусе это уже видно: на перифразах вроде «нужен человек, который свяжет CRM и мессенджер через no-code» полнотекстовый поиск не возвращает ничегоwebsearch_to_tsquery требует все значимые слова разом, — а семантический возвращает ровно те вакансии, где про no-code не сказано ни разу. Ради этой разницы pgvector в проекте и стоит.

Разметка лежит в eval/queries.yaml и составлена с уклоном в то, что ключевыми словами не находится: перифразы, описания задачи вместо названия технологии, запросы про условия и про уровень кандидата.

Ограничения

Пишу прямо, потому что проверять всё равно будут:

  • Это личный проект, а не продакшен под нагрузкой. Написан для собственного поиска работы.

  • Нужен токен приложения hh. Без него доступны только справочники; вакансии не отдаются никому анонимно.

  • Курсы валют статичные. Приведение зарплат к рублям — приближение для сортировки, а не финансовый расчёт. Тянуть курсы ЦБ ради этого — усложнение без пользы.

  • Выгрузок вакансий в репозитории нет. База живёт локально. В samples/ лежит демонстрационный корпус на 52 вымышленные вакансии — его кладёт seed и на нём собрана витрина; настоящих объявлений здесь нет ни одного.

  • Модель эмбеддингов выбрана по весу, а не по качеству. Из многоязычных, доступных в fastembed, paraphrase-multilingual-MiniLM-L12-v2 — самая лёгкая (220 МБ против 2.24 ГБ у multilingual-e5-large). Насколько это стоит recall, видно в разделе выше.

  • compare_to_profile не проверяет требования к переезду и английскому: в данных hh нет полей, по которым это можно утверждать, а гадать по ключевым словам в описании — врать с уверенным лицом.

Разработка

# uv sync приводит окружение в точное соответствие набору extra,
# поэтому для разработки нужны оба: без rag не соберётся семантический поиск,
# без dev не будет pytest.
uv sync --extra dev --extra rag

uv run pytest -q                 # юнит-тесты, база не нужна
uv run ruff check src tests
uv run mypy
uv run alembic check             # схема и модели не разошлись

Тесты, которым нужна живая база или настоящая модель, помечены маркерами:

uv run pytest -m integration     # требует docker compose up -d db
uv run pytest -m embeddings      # скачивает модель

Лицензия

MIT — см. LICENSE.

Available Tools

7 tools
compare_to_profileA

Честно сопоставляет вакансию с профилем кандидата (profile.yaml): verdict good/stretch/no, совпавшие и недостающие навыки, а если verdict «no» — конкретные причины отказа (blockers): зарплата ниже минимума, требуемый опыт выше принимаемого, не подходящая занятость, не удалённая работа, стоп-слово из red_flags. Используйте перед тем, как рекомендовать вакансию кандидату как хороший вариант — не оценивайте пригодность вакансии самостоятельно. По умолчанию грузит profile.yaml из корня проекта (шаблон — profile.example.yaml); profile_path позволяет указать другой файл.

ParametersJSON Schema
NameRequiredDescriptionDefault
vacancy_idYes
profile_pathNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the burden. It discloses the core behavior, output details, and blockers list, plus the default profile loading behavior and profile_path override. It omits potential failure modes like a missing profile.yaml, which would be useful, but overall it is transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences cover purpose, output, usage guidance, and parameter details without redundancy. Front-loaded with the main comparison action and outcome, making it easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists, so return values are covered. The description provides enough context for an agent to know when to call it, what inputs matter, and what to expect. It could mention error conditions like missing profile, but this is a minor gap for a comparison tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description explicitly explains profile_path (default, template). Vacancy_id is only implied as 'вакансию' and not described in terms of source or format, so the description partially compensates for the lack of schema documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the specific verb 'сопоставляет' and resource (vacancy vs candidate profile), and enumerates concrete outputs: verdict, matched/missing skills, and blockers. This distinguishes it from sibling tools focused on search, market analysis, or database status.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs when to use: 'Используйте перед тем, как рекомендовать вакансию кандидату как хороший вариант' and warns against self-assessment. Does not name specific alternative tools, but the directive is clear enough for an agent to decide appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

db_statusA

Служебная информация о состоянии базы: сколько вакансий/работодателей/навыков загружено, сколько описаний уже проиндексировано эмбеддингами, какой моделью они посчитаны, за какой период данные и когда их в последний раз собирали. Вызывайте в начале работы, чтобы понимать границы доступных данных и не выдумывать вакансии или статистику, которых в базе нет. Параметров не принимает.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations present, the description carries the full burden, and it does well by stating the tool is a service/inspection call, clarifying it accepts no parameters, and explaining what data boundaries it reveals. It stops short of explicitly saying it is read-only or describing the response structure, but the output schema covers return-format details.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: the first sentence packs the substantive status contents, the second gives the usage rationale, and the final short clause confirms zero parameters. Every sentence earns its place and there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter status tool, this is complete: it tells the agent what data it will receive, why it matters, and when to invoke it relative to other work. The existence of an output schema compensates for any missing return-format details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the schema already fully describes the input contract. The description reinforces this with the explicit statement that it takes no parameters, which is sufficient for a parameterless tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies a specific verb and resource: it provides status information about the database. It enumerates the exact contents (counts of vacancies/employers/skills, embedding index status, model, data period, last collection time). This differentiates it from the sibling search/analytics tools and leaves little ambiguity about its role.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly instructs the agent to call this tool at the beginning of work to understand data boundaries and avoid fabricating vacancies or statistics. While it does not name alternatives or specify when not to use it, the directive is concrete and actionable for a preflight status check.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_vacancyA

Полная карточка одной вакансии по её id из hh.ru: незакрытое описание, работодатель, все требуемые навыки, ссылка на hh.ru. Используйте после search_vacancies/semantic_search, чтобы изучить конкретную вакансию подробно, или перед compare_to_profile.

ParametersJSON Schema
NameRequiredDescriptionDefault
vacancy_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden and does disclose the source (hh.ru), the fact that the description is not truncated, and the returned contents. It does not mention potential latency, error conditions, access constraints, or that this is a live fetch, so the behavioral picture is only partially complete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no redundancy, and the core purpose is front-loaded before the usage guidance. Every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter read tool with an output schema and clear siblings, the description is nearly complete: it covers what is returned and when to invoke it. Minor gaps around failure modes and availability are acceptable because the tool is simple and the output schema already defines the return shape.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description compensates by specifying 'by its id' and linking the id to the output of search_vacancies/semantic_search. For a single integer parameter, this adds enough semantic meaning beyond the schema's 'Vacancy Id' title.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: retrieving the full card of one vacancy by id from hh.ru. It also enumerates the card contents (description, employer, skills, link) and implicitly distinguishes itself from sibling tools by positioning it as the detailed follow-up to search_vacancies/semantic_search.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit context: use after search_vacancies or semantic_search to study a concrete vacancy, or before compare_to_profile. It does not explicitly state when not to use it or name exclusions, but the intended workflow is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

market_overviewA

Общий срез рынка по фильтру: сколько вакансий, у скольких указана зарплата и её персентили (p25/p50/p75), доля удалённых, разбивка по требуемому опыту и топ-10 работодателей. Используйте для агрегированных вопросов вида «какие сейчас зарплаты» или «много ли удалёнки» — не для списка конкретных вакансий.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNo
area_idNo
published_within_daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It clearly reveals the aggregate nature and specifies the exact computed metrics: salary percentiles, remote share, experience breakdown, and top employers. Minor gaps like data freshness or source are not addressed, but they are not critical for this read-style aggregate tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The output summary is front-loaded and the usage caveat is placed after, making it easy to scan and act on.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is informative for an aggregate tool and there is an output schema to cover return shapes. However, the total absence of parameter semantics and the lack of clarification about what the top-10 employers are ranked by leaves invocation partially underspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema coverage is 0% and the description does not compensate. It only says 'by filter' without explaining the meaning or format of query, area_id, or published_within_days. An agent cannot reliably construct filter values from this definition alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states exactly what the tool does: it returns an aggregated market snapshot by filter, with vacancy count, salary percentiles (p25/p50/p75), remote share, experience breakdown, and top-10 employers. It also explicitly distinguishes itself from tools that return specific vacancy lists.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear when-to-use examples ('what are current salaries', 'is there much remote work') and an explicit when-not-to-use case: not for a list of specific vacancies. It does not name a specific sibling tool like search_vacancies, which keeps it just shy of full alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_vacanciesA

Полнотекстовый поиск вакансий по словам в названии и описании (Postgres tsvector, синтаксис websearch: кавычки для точной фразы, минус перед словом — исключить его). Используйте, когда запрос содержит конкретные термины: должность, технологию, компанию. Для поиска по смыслу и синонимам без точных слов используйте semantic_search. Пустой query — не ошибка, вернутся вакансии по фильтрам, отсортированные по дате публикации. Описание в каждом результате обрезано; полный текст — через get_vacancy по id.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryNo
area_idNo
experienceNo
remote_onlyNo
salary_min_rubNo
published_within_daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the burden falls on description. It discloses important behaviors: empty query not an error, sorted by publication date, resul descriptions truncated, full text via get_vacancy by id. Does not mention pagination, auth, or read-only nature, but core runtime behavior is covered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Fully front-loaded with core mechanism, then usage, then edge-case behavior and result truncation. Every sentence adds value; no filler or tautology.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 7 optional parameters with defaults and an output schema, the description covers usage conditions, query syntax, empty-query behavior, sorting, and result truncation with a pointer to get_vacancy. No critical gap for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage and 7 params, so description must compensate. It thoroughly explains query syntax (websearch, quotes, minus prefix) and mentiones filters generally. Individual filters like area_id, experience, remote_only are not semantically described, but the core parameter is well covered.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the function is full-text vacancy search by words in title and description, including the Postgres tsvector and websearch syntax. It explicitly contrasts itself with semantic_search ('Для поиска по смислy и синонимам без точных слов используйте semantic_search'), so the agent can distinguish sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to use: when the query contains concrete terms (должноst, технологию, компанию), and names the alternatives for semantic search. It also covers edge case with empty query and says what will be returned, which is actionable guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

skill_statsA

Статистика по навыкам: какие технологии чаще всего требуются в подходящих под фильтр вакансиях, их доля и медианная зарплата вакансий, где навык нужен. Используйте для вопросов «что сейчас востребовано» или «что учить». Не возвращает список вакансий — для него используйте search_vacancies.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNo
top_nNo
area_idNo
published_within_daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It explains the aggregate nature of the result, mentions share and median salary, and clarifies that no vacancy list is returned. It does not describe edge cases like empty filters or pagination, but the core behavior is disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences, front-loaded with the result, then use cases, then an exclusion with a sibling tool. Every sentence adds value and there is no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete for purpose and routing, and an output schema exists, so the return shape is covered elsewhere. However, with four parameters completely undocumented and no annotations, the description is not fully sufficient for an agent to construct a correct call confidently.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description does not explain what query, top_n, area_id, or published_within_days mean semantically. It only refers vaguely to "фильтр" (filter). An agent receives almost no guidance on how to populate these parameters correctly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool computes skill statistics: which technologies appear most often in matching vacancies, their share, and median salaries for vacancies requiring each skill. It also distinguishes itself from search_vacancies by explicitly saying it does not return a vacancy list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells the agent when to use this tool: for questions like "what is currently in demand" or "what to learn". It also gives an exclusion and names the alternative tool, search_vacancies, for retrieving vacancy lists.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.4/5.0
Disambiguation5/5

Each tool targets a distinct function: lexical vs semantic search, individual vacancy detail, skill statistics, market aggregates, candidate-fit comparison, and database metadata. The overlapping search/stat tools explicitly cross-reference each other to prevent misselection.

Naming Consistency4/5

Most names are lowercase snake_case and use clear noun/verb tokens, but the pattern is mixed: search_vacancies/get_vacancy/compare_to_profile are verb-first, while skill_stats/market_overview/db_status are noun-first. Still predictable and readable.

Tool Count5/5

Seven tools cover the job-search niche without redundancy; each one serves a clear use case and none feel like filler.

Completeness5/5

The surface covers the full workflow: discover via search/semantic search, inspect via get_vacancy, analyze via skills/market tools, evaluate fit via compare_to_profile, and understand data boundaries via db_status. No critical missing operation for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to search job vacancies, manage resumes, and apply to jobs on HeadHunter (hh.ru), Russia's largest job search platform. Includes OAuth 2.0 integration for secure job applications and an automated vacancy hunter agent with intelligent matching.
    27
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Integrates with HuntFlow ATS to manage vacancies, candidates, resumes, and recruitment stages via 7 tools and 2 skill prompts.
    7
    50
    1
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    Enables AI assistants to access and manage HeadHunter job platform data, including vacancies, resumes, negotiations, and employer settings via 167+ tools.
    298
    5
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    Enables to interact with hh.ru (a Russian job platform) through browser automation, allowing users to search for jobs, manage resumes, apply to vacancies with cover letters, and track application statuses via natural language.
    9
    3

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Denmurzik/hh-radar'

If you have feedback or need assistance with the MCP directory API, please join our Discord server