Skip to main content
Glama
filesystem-mcp-server

filesystem-mcp-server

by GAVTIN

Resume Matcher — MCP Edition

Агент сопоставления резюме, использующий прямые инструменты файловой системы, заменён на автономный MCP-сервер, плюс агент LangGraph, рефакторизованный для общения с ним (и со вторым MCP-сервером) через реальный MCP-клиент вместо локальных вызовов функций.

Учебные цели → что в этом репозитории

Цель

Где

Понять Model Context Protocol

filesystem_mcp_server.py реализует инструменты и ресурсы; протестирован против реального протокола JSON-RPC 2.0 в tests/ (не мокается)

Заменить пользовательские инструменты MCP-серверами

Каждая операция файловой системы из Milestone 1 теперь является @mcp.tool(); ничто в matching_agent.py не импортирует код файловой системы напрямую

Реализовать стандартизированные интерфейсы инструментов

Единая обёртка {"success": bool, ...} для каждого инструмента; структурированные коды ошибок в стиле JSON-RPC (см. ниже)

Развернуть production-ready системы

Конфигурация на основе env, ограниченная конкурентность, обработка частичных сбоев, протестированное исправление передачи env, 14 проходящих тестов на уровнях unit и протокола

Related MCP server: Filesystem MCP Server

Архитектура

flowchart LR
    subgraph "Agent process (matching_agent.py)"
        A["LangGraph StateGraph"] --> B["MultiServerMCPClient"]
        A --> L["Claude (LLM)\nstructured scoring"]
    end
    B <-->|"JSON-RPC 2.0 / stdio"| C["filesystem_mcp_server.py"]
    B <-->|"JSON-RPC 2.0 / stdio"| D["notifications_mcp_server.py"]
    C --> E[("sample_data/resumes/\nresults/")]
    D --> F[("results/notifications.log")]

Два независимых MCP-сервера, каждый в отдельном процессе ОС, каждый не знает о другом или о LangGraph. Агент обнаруживает их инструменты при запуске (client.get_tools()) и вызывает их по имени — в этом и заключается суть рефакторинга: filesystem_mcp_server.py может завтра получить новый инструмент, и matching_agent.py не потребует изменения кода.

Конечный автомат (взаимодействие агента и MCP)

stateDiagram-v2
    [*] --> check_new_resumes
    check_new_resumes --> batch_extract: new files found
    check_new_resumes --> [*]: nothing new — short-circuit
    batch_extract --> match: text extracted
    match --> rank_and_save: LLM structured scoring
    rank_and_save --> notify: results persisted
    notify --> [*]: done

    note right of check_new_resumes
        filesystem server
        tool: watch_directory
    end note
    note right of batch_extract
        filesystem server
        tool: batch_process
    end note
    note right of rank_and_save
        filesystem server
        tool: save_match_result (per match)
    end note
    note right of notify
        notifications server
        tool: send_match_notification
        (only matches scoring >= 70)
    end note

То, что check_new_resumes вызывает watch_directory первым — до любых других действий — намеренно: запуск без новых данных сразу переходит к [*], не тратя вызов LLM, а режим --watch (см. ниже) повторно обрабатывает только то, что действительно изменилось, а не весь каталог каждый раз.

Запуск локально

Из корня репозитория создайте виртуальное окружение и установите зависимости.

Bash / Git Bash / Linux / macOS

cd [Path To Files]
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
export ANTHROPIC_API_KEY="<your-anthropic-api-key>"
python matching_agent.py

Windows PowerShell

cd [Path To Files]
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
$env:ANTHROPIC_API_KEY = "<your-anthropic-api-key>"
python .\matching_agent.py

Если нужно заново просканировать входящие с нуля

Наблюдатель хранит небольшой файл состояния, поэтому он оценивает только недавно добавленные резюме. Если нужно обработать текущую папку снова, сначала очистите состояние наблюдателя:

python matching_agent.py --reset-watch

Типичные сценарии использования

# one pass over the bundled sample data
python matching_agent.py

# run against your own job description and resume folder
python matching_agent.py --job-description path/to/jd.txt --resume-dir path/to/resumes

# keep polling for newly added resumes every 15s (Ctrl+C to stop)
python matching_agent.py --watch --interval 15

Используйте Python из виртуального окружения проекта, а не системный Python, при запуске агента. В этом репозитории рабочая команда обычно ./.venv/Scripts/python.exe matching_agent.py на Windows или source .venv/bin/activate && python matching_agent.py на Unix-подобных оболочках.

Запустите любой сервер отдельно, чтобы проверить его напрямую (удобно с MCP Inspector):

python filesystem_mcp_server.py
python notifications_mcp_server.py

Конфигурация управляется через env — см. ServerConfig.from_env() в filesystem_mcp_server.py:

Переменная

По умолчанию

RESUME_DIRECTORY

./sample_data/resumes

RESULTS_DIRECTORY

./results

ALLOWED_EXTENSIONS

.txt,.pdf,.docx

MAX_BATCH_CONCURRENCY

5

NOTIFY_SCORE_THRESHOLD

70 (matching_agent.py)

MATCHING_AGENT_MODEL

anthropic:claude-sonnet-5

Тестирование

pytest tests/ -v

14 тестов, два уровня:

  • Unit (test_filesystem_mcp_server.py, большая часть): вызов функций инструментов напрямую с фикстурой tmp_path — быстро, без подпроцессов. Покрывает успешные пути, коды ошибок RESUME_NOT_FOUND / INVALID_PARAMS и отчёт о частичных сбоях batch_process.

  • Protocol (test_server_speaks_mcp_protocol_over_stdio): запускает реальный сервер как подпроцесс и управляет им через официальный mcp клиентский SDK — tools/list, tools/call, resources/read — по реальному JSON-RPC 2.0, так что проверяется именно протокольный слой, а не только Python под ним.

  • Agent (test_matching_agent.py): вызов LLM заменён на детерминированный фейк (FakeStructuredModel), поэтому этим тестам не нужен API-ключ — они проверяют связку графа, обнаружение инструментов на нескольких серверах, путь короткого замыкания при отсутствии новых файлов и то, что второй проход в стиле --watch обрабатывает только вновь поступивший файл, а не весь каталог.

Проектные решения

MCP SDK закреплён на mcp>=1.28,<2.0. Линейка v2 Python SDK вышла вместе с ревизией спецификации MCP от 2026-07-28 и переименовывает FastMCP в MCPServer (теперь в mcp.server.mcpserver). v1.x — это то, на чём построена и документирована текущая экосистема LangChain/LangGraph MCP, поэтому проект намеренно закрепляется на ней, а не случайно — стоит пересмотреть, когда langchain-mcp-adapters и более широкая база учебных материалов догонят v2.

stdio вместо HTTP. Нет сетевой поверхности для защиты, не нужно настраивать аутентификацию, и это то, что MultiServerMCPClient ожидает для локального "command" сервера. Компромисс, подтверждённый при создании: каждый вызов инструмента открывает новый сеанс подпроцесса, а не переиспользует существующий — нормально для демо/CLI-агента, и это честная причина, по которой чувствительная к задержкам production-версия перешла бы на долгоживущий streamable-http сервер.

Ошибки — это структурированный JSON, а не проза. Каждый сбой вызывает ToolError с JSON-нагрузкой, содержащей code в диапазоне "server error" JSON-RPC (-32000..-32099) плюс машиночитаемую метку error (RESUME_NOT_FOUND, DIRECTORY_NOT_FOUND, UNSUPPORTED_FILE_TYPE, EXTRACTION_FAILED, INVALID_PARAMS). Подтверждено сквозным тестом с живой клиентской сессией: это проявляется как CallToolResult(isError=True, ...), а _call_tool() в matching_agent.py повторно поднимает его как MCPToolCallError с сохранённым кодом, вместо того чтобы вызывающему приходилось сопоставлять строки сообщения.

watch_directory опрашивает, а не отправляет. Инструменты MCP — это запрос/ответ, поэтому это опрос (JSON-файл состояния имя_файла→mtime, сравниваемый при каждом вызове), а не слушатель inotify/watchdog. Режим --watch в matching_agent.py превращает это в нечто, ощущаемое как живое — фоновый слушатель, отправляющий через MCP подписки на ресурсы, был бы естественным следующим шагом и поддерживается протоколом, но выходит за рамки этого проекта.

batch_process принимает явный список файлов, а не только каталог. Именно это позволяет check_new_resumes → batch_extract повторно обрабатывать только то, что только что сообщил watch_directory, а не всю папку каждый раз — конкурентность (asyncio.Semaphore(MAX_BATCH_CONCURRENCY)) делает "эффективность" из спецификации реальной, когда список длинный.

Передача env явная, и это не опция по умолчанию, которую можно пропустить. Реальная ловушка, обнаруженная при создании: stdio-клиент mcp не наследует окружение родительского процесса — он запускает дочерние серверы с минимальным окружением по умолчанию (только PATH/HOME/TERM), что подтверждено напрямую через mcp.client.stdio.get_default_environment(). Без явной передачи env=dict(os.environ) в конфигурации сервера в matching_agent.py, RESUME_DIRECTORY и другие переменные молча никогда не достигают filesystem_mcp_server.py — агент запускается, отлично обнаруживает инструменты и просто тихо работает с неправильным каталогом. Стоит знать, прежде чем это обойдётся вам в сеанс отладки.

Структура репозитория

resume-matcher-mcp/
├── filesystem_mcp_server.py      # Part A
├── notifications_mcp_server.py   # Part B bonus: 2nd MCP server
├── matching_agent.py             # Part B
├── requirements.txt
├── pytest.ini
├── tests/
│   ├── test_filesystem_mcp_server.py
│   └── test_matching_agent.py
├── sample_data/
│   ├── job_description.txt
│   └── resumes/                  # 21 resumes, deliberately strong/partial/weak fit
└── results/                      # match_results.jsonl + notifications.log (gitignored)

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables file system operations (read, write, list, search, watch, batch process) via MCP over JSON-RPC 2.0, used by a resume matching agent.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides file system tools for resume matching agents, enabling reading, writing, searching, listing, watching, and batch processing of files via the Model Context Protocol.
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Provides MCP tools for reading, listing, writing, searching, watching, and batch-processing files, enabling automated file management and resume matching workflows.
    -