mcp-job-intel
Agentic Job Intelligence Pipeline (MCP + LLM)
Агентный конвейер, который использует Model Context Protocol для оркестрации внешних инструментов с целью структурированного получения данных, с уровнем оценки на основе LLM, который ранжирует неструктурированные описания вакансий относительно профиля кандидата. Давление на окно контекста обрабатывается каскадной стратегией «сначала метаданные» (pipeline.py), а те же инструменты доступны также настоящему агенту с вызовом инструментов, имеющему собственный цикл планирования (agent.py), и REST + WebSocket API (api.py). Полное описание — в docs/.
Запуск
pip install -r requirements.txt
python pipeline.py --benchmark # token comparison, zero API calls
python pipeline.py --dry-run # real MCP subprocess handshake, no LLM
export OPENAI_API_KEY=sk-...
python pipeline.py --top 8 # fixed 3-stage pipeline
python agent.py --dry-run # agent tool discovery, no LLM calls
export OPENAI_API_KEY=sk-...
python agent.py --top 8 # tool-calling agent with a planning loop
python eval.py --prefilter-only # stage-1 recall, deterministic half, no key needed
pytest -q # in-process MCP server, no key needed
uvicorn api:app --reload # REST + WebSocket layer, http://localhost:8000
curl localhost:8000/health
curl -X POST localhost:8000/rank -H 'content-type: application/json' -d '{"use_llm": false}'Измеренный результат
Корпус из 150 вакансий, шорт-лист из 8. prefilter() применяет фильтры по тегам/заголовку, уровню (отбрасывает senior, если опыт кандидата < 4 лет) и местоположению (предпочтительный город кандидата, нормализованные алиасы или удалёнка), что сокращает число выживших с 150 до 31:
Стратегия | Промпт-токены | против наивного |
A — отправить все 150 полных описаний | 67,360 | — |
B — сначала метаданные, затем получить 8 | 13,802 | 4.9× дешевле |
C — prefilter → метаданные → получить 8 | 6,258 | 10.8× дешевле |
Воспроизводится командой python pipeline.py --benchmark. Реальные числа из data/jobs.json этого репозитория на сегодняшний день, не заглушки. Подсчёт токенов использует энкодер o200k_base из tiktoken (тот, что на самом деле используют gpt-4o / gpt-4o-mini) — точный, не оценочный. Старая эвристика «символы ÷ 4» завышала стоимость наивной стратегии на 17.4% на этом корпусе; поле heuristic_vs_real_tokens в benchmark() воспроизводит это сравнение.
Архитектура
MCP SERVER (stdio subprocess) MCP CLIENT / pipeline.py
--------------------------------- ------------------------------------
tool list_jobs -> metadata <---- Stage 0 prefilter() [0 tokens]
tool get_job_details -> full text Stage 1 shortlist [~5k tokens]
tool get_candidate_profile Stage 2 score [~4k tokens]
tool corpus_stats
resource jobs://schema Meter tracks tokens per stage
prompt rank_jobsАргумент в пользу поэтапного извлечения
Наивный подход: отдать модели каждое полное описание и попросить ранжировать. Три проблемы.
Стоимость — 79k промпт-токенов за запуск, и она растёт линейно с ростом корпуса.
Потолок — после нескольких сотен вакансий это просто превышает окно контекста. Не медленно: невозможно.
Качество — полнота при длинном контексте ухудшается в середине большого промпта, поэтому ранжирование становится хуже с добавлением новых кандидатов.
Поэтапное извлечение, сначала самый дешёвый фильтр:
Стадия | Механизм | Стоимость | Почему здесь |
0 | Детерминированный фильтр по тегам/заголовку/местоположению на Python | бесплатно | Не позволяйте модели читать то, что |
1 | LLM видит ~55 токенов метаданных на каждую вакансию, выбирает топ-8 | ~5k | Отсев с высокой полнотой. Предписано включать с запасом, потому что стадия 2 может отклонить. |
2 | Полные описания только для 8 выживших | ~4k | Полная точность, оплачивается один раз, только там, где это меняет ответ. |
Обобщаемый принцип — и то, что стоит произнести вслух на собеседовании — каскад по стоимости: располагайте фильтры от самых дешёвых к дорогим и настраивайте порог каждой стадии на полноту, а не на точность, потому что более поздняя стадия ещё может отклонить, но ничто не вернёт то, что было отброшено на ранней стадии.
Агентный уровень (agent.py)
pipeline.py — это фиксированный скрипт: сначала prefilter, затем всегда shortlist, затем всегда score. agent.py передаёт модели те же MCP-инструменты через OpenAI function calling и позволяет ей планировать собственный путь — настоящий агент с вызовом инструментов, а не жёстко заданная последовательность:
Структурированный финальный ответ в виде вызова инструмента. Агент не «отвечает в JSON и надеется» — завершение означает вызов синтетического инструмента
submit_rankings, схема параметров которого являетсяschemas.RankingResult. Неверные аргументы возвращаются как ошибка валидации, которую модель может прочитать и исправить, в течение ограниченного числа повторных попыток.Сбои инструментов деградируют, а не роняют систему. Любое исключение MCP-инструмента становится обычным результатом инструмента
{"error": ...}, который возвращается модели, чтобы она могла обойти неудачный вызов, а не обрушить весь запуск.Модель, которая так и не сходится, всё равно возвращает что-то. Если она исчерпывает бюджет шагов/повторов без корректного вывода, агент откатывается к той же детерминированной логике
prefilter → shortlist → score, что иpipeline.py, и сообщаетfallback_used: true.Временные ошибки API получают собственные повторные попытки через
tenacity, отдельно от цикла повторов по схеме выше — плохое соединение и плохой ответ это разные виды сбоев.
Пошаговый цикл описан в docs/CODE_WALKTHROUGH.md.
Уровень REST + WebSocket (api.py)
Сервис FastAPI оборачивает MCP-инструменты, pipeline.py и agent.py, чтобы они были доступны по HTTP, а не только как CLI-скрипты:
Endpoint | Что делает |
| проверка работоспособности |
| список метаданных / полные детали; та же инвариантность без описаний, что и у MCP-инструмента |
| статистика корпуса |
| запуск конвейера ранжирования ( |
| то же, что |
Одна сессия MCP stdio открывается один раз при запуске и разделяется за блокировкой (api.MCPSession), а не порождает подпроцесс на каждый запрос — это осознанное упрощение по сравнению с настоящим пулом соединений, о чём сказано в docstring модуля api.py, и это не выдаётся за реальную распределённую систему. Каждый запрос получает корреляционный идентификатор (request_id), проходящий через логи и все потоковые события, чтобы запуск можно было проследить через асинхронные переходы.
Заметки по MCP, которые стоит знать назубок
Зачем это существует: N моделей × M интеграций превращается в N + M. Один протокол, JSON-RPC 2.0 поверх stdio или Streamable HTTP.
Инструменты против ресурсов против промптов: управляемые моделью / приложением / пользователем. Правильно разобраться с этим трио — частый способ выделиться на собеседовании.
Форма
CallToolResult:content(блоки),structured_content(типизированный, оборачивается в{"result": ...}для возврата не-объектов),is_error. См.pipeline.call().Дизайн инструментов — это дизайн API для нечеловеческого вызывающего.
list_jobsиget_job_detailsразделены, потому что именно это разделение обеспечивает поэтапное извлечение. Docstring'и и есть описание инструмента, которое читает модель — расплывчатый docstring, неправильный выбор инструмента.Пакетные параметры вместо скалярных:
get_job_details(job_ids: list[str])стоит одного кругового обращения;get_job_detail(job_id: str)— восьми.
Известные ограничения
Полнота шорт-листа (сохраняет ли стадия 1 размеченные как релевантные вакансии, пережившие prefilter?) требует настоящего
OPENAI_API_KEYдля измерения —python eval.py --top 8запускает его; здесь не запускалось из-за стоимости.Корпус синтетический. Реальные вакансии грязнее — HTML, дубликаты, устаревшие объявления.
Нет кэширования между запусками, поэтому повторные вызовы снова платят за стадию 1.
Полнота стадии 1 — измеренная, а не предполагаемая
В data/relevance_labels.json — 20 ID вакансий, которые человек счёл бы релевантными кандидату, выбранные по документированным воспроизводимым критериям (см. файл). eval.py проверяет две вещи отдельно:
prefilter_recall— сколько из 20 размеченных релевантных вакансий переживают детерминированный prefilter? Бесплатно, без API-ключа:python eval.py --prefilter-only→ 20/20, recall 1.0. Критерии разметки — строгое подмножество собственных фильтров prefilter, так что это подтверждает, что prefilter не молча отбрасывает целевую роль, а не просто предполагает это.shortlist_recall— сколько из них также переживают шорт-лист LLM при томtop, с которым вы реально запускаете?python eval.py --top 8— требуетOPENAI_API_KEY, реального вызова модели, поэтому в этом репозитории не запускается; запустите сами, когда у вас будет ключ.
Ваши задачи
prefilter()— добавьте фильтры по уровню и местоположению. Перезапустите--benchmark, зафиксируйте число.Готово: 150 → 31 выживших, сокращение в 10.8× по сравнению с наивным.Заменитеapprox_tokensна реальный подсчёт черезtiktoken; отметьте, насколько эвристика ÷4 была неточна.Готово: эвристика завысила на 17.4%.Создайте размеченный набор из 20 вакансий и измерьте полноту стадии 1.Готово для бесплатной половины (prefilter_recall= 1.0); платная половина (shortlist_recall) подключена вeval.py --top 8, запустите со своим ключом.Подключите сервер в MCP-конфиг Claude Desktop и вызовите его вручную.Пример конфига и инструкции по перезапуску — вdocs/OVERVIEW.md— фактическая регистрация происходит в вашем собственном приложении Claude Desktop, а не в этом репозитории.
Документация
docs/OVERVIEW.md— что это за проект, какую проблему он решает и почему, архитектура, подключение Claude Desktop, локальная тестируемость, известные ограничения.docs/CODE_WALKTHROUGH.md— каждый модуль, функция за функцией.
This server cannot be installed
Maintenance
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
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
GetJobzi MCP server for job search, application tracking, and career forecasting.
MCP server for AI job search — find jobs, track applications, get alerts. Claude, ChatGPT, Cursor.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/jaideepdnaik/mcp-job-intel'
If you have feedback or need assistance with the MCP directory API, please join our Discord server