Skip to main content
Glama
Work Researcher MCP

Work Researcher MCP

by Andrem19

Work Researcher Bot

Серверный агент поиска работы для одного кандидата — Andrey Remnev. Каждый вечер в 20:00 по времени United Kingdom он обновляет четыре профильных CV из публичной папки Google Drive, ищет entry-level вакансии, фильтрует агентства и неподходящую географию, оценивает вакансии через glm-5.3-flash и отправляет ранжированный HTML-отчёт в Telegram.

Четыре карьерные линии

  1. Data Engineering

  2. Geospatial Data Engineering

  3. Analytics → Data Engineering

  4. Software Engineering → Data Platform

Ищутся junior, trainee, graduate, associate и Level 1 роли без требования значительного подтверждённого опыта. AI/MLOps остаётся последующей надстройкой, а не отдельной пятой линией.

Related MCP server: swissdevjobs

Политика отбора

  • только прямые работодатели; агентства и recruitment consultancies исключаются;

  • remote — вся Великобритания;

  • on-site — Blackpool, Preston и ближайшие населённые пункты;

  • hybrid — расширенная зона до Manchester и сопоставимых городов;

  • senior/lead/manager роли, требования 3+ лет и платные training/course ads исключаются;

  • GLM ранжирует, проверяет обязательные и желательные требования, специальные условия, соответствие выбранному CV и формирует русское резюме; после пакетной проверки отдельный проход сравнивает весь shortlist на единой шкале; модель ищет явные entry-сигналы (обучение, mentoring, приглашение кандидатов, которые соответствуют не всем критериям); неизвестный автор объявления допускается только после подтверждения моделью прямого работодателя;

  • вакансии с явной пометкой closed/expired или истёкшим указанным closing date исключаются до отчёта, а близкий deadline повышает срочность рассмотрения;

  • уже доставленные вакансии хранятся в SQLite, но остаются кандидатами, пока актуальны: новые варианты получают преимущество, а сильные повторные заполняют топ и помечаются как ранее показанные;

  • линия определяется по названию и содержанию вакансии, независимо от поискового запроса; только после этого выбирается соответствующий CV;

  • Reed учитывает локальную географию, фильтр direct employers и пагинацию; Indeed UK собирается отдельным поддерживаемым адаптером, а Totaljobs имеет публичный reader fallback вместо прежнего server-side timeout. Мягкие квоты в shortlist и топе сохраняют разнообразие источников/линий, но не оставляют пустые места, если других подходящих вариантов нет.

Runtime

Перед GLM бот заново открывает все прошедшие фильтры объявления и цепочки ссылок подачи (только GET; без отправки заявок). Срок работодателя приоритетнее срока размещения на площадке; сохраняются источник, время проверки и расхождения. Проверяются validThrough, даты/время по Europe/London, 404/410 и closed/expired. Год, отсутствующий в объявлении, принимается текущим и явно помечается в отчёте. CAPTCHA, недоступная или JS-only страница не считаются подтверждением приёма: такие варианты помечаются предупреждением, а при сроке закрытия сегодня удерживаются из отчёта до подтверждения. GLM не может переписать эти факты. Перед отправкой срок повторно сравнивается с текущим временем.

В каждой Telegram-карточке (включая краткие 6–10) есть строки «Опубликовано» и «Дедлайн». Для дублей сравниваются даты на найденных площадках и связанных страницах работодателя: основной становится самая ранняя известная публикация. Отдельная таблица vacancy_publications хранит доказательства по каждому URL, поэтому перепубликация и более длинное описание не затирают раннюю дату. first_seen, дата скачивания и dateModified/Jooble updated публикацией не считаются. Приблизительные даты и отсутствующий год помечаются; при равенстве дат без времени не заявляется, какая площадка была первой в течение дня. Это самая ранняя дата среди найденных источников, а не доказательство первой публикации во всём интернете. Если старая копия закрыта, она остаётся ссылкой-источником даты, а основная ссылка ведёт на актуальную копию.

Повторная проверка сохранённого отчёта без GLM и Telegram:

uv run python scripts/verify_report_freshness.py /path/to/nightly-runs/report.json
20:00 Europe/London
  -> public Google Drive: download + validate exactly four non-geology CVs
  -> стабильные запросы + GLM-план дополнительных названий ролей из каждого CV
  -> отдельные national, remote UK и Blackpool/Preston/Manchester проходы
     across configured providers, включая Indeed, Totaljobs, GOV.UK Find a job
     и Civil Service Careers
  -> детерминированные фильтры только для явных senior/agency/training/geo отказов
  -> advert + application-link freshness verification
  -> GLM-5.3-Flash full-description assessment
  -> one global comparative rerank of every hard-filtered candidate
  -> Telegram HTML header + one detailed vacancy card per message
  -> delivery state saved only after Telegram succeeds

Telegram-отчёт содержит максимум 10 вакансий: первые 5 приходят полными карточками с требованиями и анализом CV, позиции 6–10 — сокращёнными карточками с ключевыми данными и ссылкой. Точная копия сформированного отчёта, распределение источников и Telegram message IDs сохраняются в data/nightly-runs/ для последующего аудита качества.

Еженедельное исследование рынка

Каждую пятницу в 20:00 UK time отдельный work-researcher-market.timer исследует рынок по матрице 4 карьерных направления × 3 уровня: Entry, Middle и High-paying (£80k+). GLM-5.3-Flash подтверждает релевантность и уровень роли, а воспроизводимый Python-анализ рассчитывает спрос на технологии, salary coverage, P25/P50/P75, распределение по квартилям и наиболее частые/дорогие пары и тройки технологий. Поиск использует 90-дневное rolling window и расширенную сетку из 90 role-запросов. История сохраняет до 104 недель спроса и медианной зарплаты для каждой технологии и комбинации.

Последний snapshot и история хранятся вне release в /var/lib/work-researcher-bot/market. Dashboard публикуется существующим Nginx: https://devbot.remart.ovh/jobs/.

Локальные команды

uv sync --extra dev
uv run work-researcher doctor
uv run work-researcher sync-drive
uv run work-researcher run-once --dry-run
uv run work-researcher weekly-market
uv run pytest -q

Скопируйте config.example.toml в config.toml. Секреты задаются только через переменные окружения: ZAI_API_KEY, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID и необязательные API-ключи job boards.

Доставка

Push в main запускает .github/workflows/deploy.yml: тесты, сборку release, SSH-загрузку на сервер и атомарное переключение /opt/work-researcher-bot/current. Systemd timer использует Europe/London, поэтому BST/GMT учитываются автоматически. Подробности — в SETUP.md.

Отделённый application-контур

Унаследованные MCP-инструменты для браузера и подачи заявок сохранены в server.py, но nightly-сервис их не импортирует и не запускает. Их можно использовать вручную/локально через work-researcher serve; автоматическая подача заявок на сервере не входит в этот runtime.

Основные компоненты

src/work_researcher/bot.py       nightly orchestration
src/work_researcher/drive.py     public Drive CV snapshot
src/work_researcher/career.py    hard eligibility filters
src/work_researcher/llm.py       GLM structured assessment
src/work_researcher/market.py    weekly market statistics and publication
src/work_researcher/telegram.py  Telegram HTML reports
dashboard/index.html             static interactive market dashboard
src/work_researcher/providers/   vacancy sources
src/work_researcher/server.py    separate local MCP/application surface
deploy/                          systemd configuration and activation

License: MIT.

Available Tools

29 tools
browser_clickA

Click element #n → returns the FRESH snapshot (popups become the active tab automatically).

Inputs: n (required), timeout_ms.

browser_closeA

Close the browser (the login profile persists on disk).

Inputs: no named parameters.

browser_evalA

Evaluate JS on the page (extract hidden JSON, scroll, dismiss cookie banners).

Inputs: js (required).

browser_formA

The dominant form with human labels per field (n, tag, type, label, required, options) + likely submit buttons. Use once per application page, then browser_set fields by number.

Inputs: no named parameters.

browser_loginA

Ensure we're signed in on a job board before applying. Opens the site; if signed out, walks 'Continue with Google' and picks the pre-approved account (active profile auth.google_account — the user allows this WITHOUT asking). Returns logged_in; needs_user=true on 2FA/ captcha/consent — then stop and ask the user to finish in the window.

Inputs: url (required).

browser_openA

Open a URL in the persistent application browser; returns the first snapshot. Logins survive between runs. Headed by default so the user can handle 2FA/captcha.

Inputs: url (required), headless.

browser_pressA

Press a page-level key (Enter, Tab, Escape) → fresh snapshot.

Inputs: key (required).

browser_screenshotA

Save a PNG to data/screenshots — pass the path as evidence to record_application.

Inputs: name, full_page.

browser_setA

Set element #n to a value — one verb for every control: inputs → fill; selects → pick option value; checkboxes/radios → true/false → returns fresh snapshot.

Inputs: n (required), value (required).

browser_snapshotA

Look at the page: numbered interactive elements + text. focus: 'inputs'|'buttons'|'links'; filter_text narrows by name (find 'Apply'); text_chars=0 → elements only, 6000 → reading mode. modal_only=true → ONLY the active dialog/wizard (question text + controls, hidden templates excluded) — use for apply wizards, then browser_set/click by number.

Inputs: focus, modal_only, text_chars, filter_text.

browser_tabsA

Tabs: list / select / close. Click-opened popups become active automatically — use this to switch back.

Inputs: index, action.

browser_typeA

Type into #n key-by-key (fires JS handlers); submit=true presses Enter → fresh snapshot. Use for search boxes and chat fields.

Inputs: n (required), text (required), submit.

browser_uploadA

Click upload control #n and send a local file (the CV path from start_application) → fresh snapshot.

Inputs: n (required), file_path (required).

browser_waitB

Wait for time / text to appear ('Application submitted') / text to disappear (spinners) → returns a snapshot.

Inputs: text, seconds, text_gone.

check_appliedA

'Have we already applied here?' — by job_id, exact URL, or fuzzy title+company (catches the same vacancy re-found on another board). ALWAYS call before a manual browser application.

Inputs: url, title, job_id, company.

fetch_job_descriptionA

Fetch FULL job descriptions by opening pages in the browser. Accepts 1-10 job_ids (BATCH - prefer batching to save round-trips). Use for jobs where the parser returned null/short descriptions (common on Totaljobs/Reed). Stores descriptions back and re-runs the requirements check against your CVs. Also detects closed vacancies ("no longer accepting applications").

Inputs: job_ids (required).

get_jobA

Details for selected jobs with adaptive batch sizing. Pass the same context_window/response_profile used for search_jobs. Compact returns up to 5 summaries or 2 full descriptions; balanced 12/5; wide 30/12. Request full descriptions only for plausible finalists. No job is deleted when a supplied job_ids list is truncated; request the remainder later.

Inputs: job_ids (required), context_window, response_profile, include_description.

get_statusA

Health/config snapshot: active candidate, local CV directory, DB stats, providers, API keys and saved searches. Call first in a session.

Inputs: no named parameters.

list_applicationsB

Application history (job, company, status, dates) — the long-term memory of everything already applied to.

Inputs: limit, status.

list_cvsA

CVs indexed from the active profile's local folder. Pass job_id to get per-CV recommendation scores for that job.

Inputs: job_id.

list_stored_jobsA

Search the LOCAL job database by criteria (no board calls). Use INSTEAD of Bash/sqlite: find all jobs from a company, filter by title keyword, location, source board, or freshness. Returns job_ids + titles + requirements status - then get_job / fetch_job_description for detail.

Inputs: limit, query, source, company, +2 more.

make_cover_letterA

Write a cover letter as a DOCX file into CV_collection and return its path — ready for browser_upload as a supporting file. Boards like Totaljobs reject files smaller than 8KB, so the document is padded through metadata if the text alone is too short. Prefer DOCX over PDF here (PDFs from minimal text are usually under the limit).

Inputs: text (required), name.

manage_blocklistA

Employer/recruiter exclusion memory. When the user says 'never apply to ' → action=add, kind=company, value=, reason=. kind=keyword blocks any vacancy whose title/description contains it. action: add | remove | list. Blocked jobs are hidden from search results and start_application refuses them until removed.

Inputs: kind, value, action, reason.

manage_profilesA

List candidate profiles or switch the active candidate.

    ``switch`` persists [general].active_profile in config.toml and takes
    effect immediately. It closes the old candidate's browser first, then
    changes the isolated CV folder, database/application history and
    browser-login folder. Always list/verify profiles before a
    search or application task for a named person.
    

Inputs: action, profile.

record_applicationA

Update an application: status planned|applying|submitted|interview| offer|rejected|withdrawn|failed; notes; evidence {screenshot: path,…}; cover_letter text.

Inputs: status (required), application_id (required), notes, evidence, +1 more.

search_jobsA

Run a UK job search OR page an earlier one. Fresh search: pass query ('Data Analyst'…) or profile ('data_analytics'/'field_geologist'). Paging: pass search_id (+offset from next_offset), preferably by copying the returned next_page object. Response sizing: pass context_window and auto selects compact (<=80k), balanced (80k-300k), or wide (>=300k). Local Qwen 3.8 27B uses context_window=78000. You may instead pass response_profile explicitly. Optional limit requests a page size within that profile's safety cap (8/20/50). With no sizing signal the default is balanced (12). All remaining results stay stored. Results are ranked, cross-board duplicates merged (sources[]), with memory flags (already_applied, application_status) and location intelligence (work_mode, distance_miles from the active profile's home, location_status ok|mismatch|caution|unknown). PAID TRAINING/COURSE ADS (where you pay them, e.g. Netcom-style 'trainee' course marketing) are excluded automatically — training_offers_skipped shows how many; set include_training=true only if the user explicitly wants courses. location_policy: 'auto' (default), 'uk_wide', 'commute_only'. work_from_home=true restricts to remote.

Inputs: limit, query, offset, profile, +15 more.

start_applicationA

Begin applying to a job. Returns the full plan: URL, apply method + site playbook, chosen CV (+alternatives), applicant profile values, step list, cautions (location mismatch, LinkedIn ToS…). REFUSES to create a second application for a job that already has one — that is the anti-double-apply memory (works across boards via dedup).

Inputs: job_id (required), cv_id, notes.

submit_job_observationsA

Feed jobs found via the harness's own browser (Indeed, CV-Library, LinkedIn, Glassdoor) into the same dedup + ranking store. Each observation: {source, url, title, company, location_text, salary_raw, description, posted_at?}. Returns stored count + duplicates merged.

Inputs: observations (required), task_id, search_id, observation_type.

sync_cvsA

Index CV files manually copied into the active profile's cv_dir. Unchanged files are skipped unless force=true. There is no cloud sync.

Inputs: force.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 29 tools

    Disambiguation4/5

    Each tool maps to a distinct job-search, application, or browser action, and search_jobs/get_job/fetch_job_description are separated by source and purpose. The main boundary that needs care is browser_type vs browser_set, but their descriptions steer usage to different control types.

    Naming Consistency4/5

    Domain tools consistently use verb_noun (search_jobs, start_application, record_application), and browser tools share a predictable browser_ prefix. A few noun-style names like browser_tabs, browser_snapshot, and browser_form break the otherwise verb-first pattern.

    Tool Count2/5

    At 29 tools, this exceeds the 25-tool threshold and feels heavy for a single MCP server. The browser_* primitives are individually distinct, but bundling a full browser automation suite with the job-application domain layer makes the surface larger than necessary.

    Completeness5/5

    The tool set covers the full workflow: searching and deduplicating jobs, fetching fuller descriptions, managing blocklists and CVs, checking prior applications, starting and recording applications, and driving the browser through manual forms. There are no obvious dead ends; truncated descriptions have a dedicated fetch tool and form interactions have snapshot/form/set/click/upload primitives.

    Maintenance

    ActivityStale
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      C
      maintenance
      Enables AI assistants to evaluate job postings under a UK visa constraint, including sponsor-licence checks with confidence grades, screening for seniority and hard requirements, and role searches that exclude recruitment agencies. It also tracks application history to help avoid duplicate applications.
      4
      72 npm
      MIT
    • F
      license
      Not graded
      quality
      A
      maintenance
      Enables an AI agent to search nine job boards across eight countries for salary-transparent tech roles, filter by pay, remote work, tech stack, and seniority, and apply directly while tracking already-applied postings so duplicates never resurface.
      -
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI agents to discover postings from watched company boards ranked by keyword fit, tailor and fact-check resumes against a source-cited profile, render ATS-readable PDFs, submit applications through the user's own browser, and track replies and follow-ups. All ranking, claim checking and email classification run as plain code, so LLM tokens are spent only on writing.
      1
      Apache 2.0
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants to autonomously search multiple job portals, score candidate-to-job fit with a dynamic 4-tier ATS engine, tailor resumes and cover letters, prepare applications, and export Word documents locally and privately.
      1
      MIT

    Appeared in Searches