filesystem-mcp-server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@filesystem-mcp-serverWatch the resumes folder and batch-extract any new files"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Resume Matcher — MCP Edition
Resume-matching agent's direct filesystem tools, replaced with a standalone MCP server, plus a LangGraph agent refactored to speak to it (and a second MCP server) through a real MCP client instead of local function calls.
Learning objectives → what's in this repo
Objective | Where |
Understand Model Context Protocol |
|
Replace custom tools with MCP servers | Every Milestone 1 filesystem operation is now an |
Implement standardized tool interfaces | Consistent |
Deploy production-ready systems | Env-based config, bounded concurrency, partial-failure handling, a tested env-forwarding fix, 14 passing tests across unit + protocol layers |
Related MCP server: Filesystem MCP Server
Architecture
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")]Two independent MCP servers, each a separate OS process, each with no
knowledge of the other or of LangGraph. The agent discovers their tools
at startup (client.get_tools()) and calls them by name — that's the
whole point of the refactor: filesystem_mcp_server.py could gain a
new tool tomorrow and matching_agent.py wouldn't need a code change.
State machine (agent ↔ MCP interaction)
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 notecheck_new_resumes calling watch_directory first — before touching
anything else — is deliberate: a run with nothing new short-circuits
straight to [*] without spending an LLM call, and --watch mode (see
below) reprocesses only what actually changed instead of the whole
directory every time.
Run locally
From the repo root, create the virtual environment and install dependencies.
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.pyWindows 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.pyIf you want to rescan the inbox from scratch
The watcher keeps a small state file so it only scores newly-added resumes. If you want to process the current folder again, clear watcher state first:
python matching_agent.py --reset-watchCommon usage patterns
# 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 15Use the project venv's Python, not the system Python, when running the agent. In this repo, the working command is typically
./.venv/Scripts/python.exe matching_agent.pyon Windows orsource .venv/bin/activate && python matching_agent.pyon Unix-like shells.
Run either server standalone to poke at it directly (handy with the MCP Inspector):
python filesystem_mcp_server.py
python notifications_mcp_server.pyConfig is env-driven — see ServerConfig.from_env() in
filesystem_mcp_server.py:
Variable | Default |
|
|
|
|
|
|
|
|
|
|
|
|
Testing
pytest tests/ -v14 tests, two layers:
Unit (
test_filesystem_mcp_server.py, most of it): call tool functions directly against atmp_pathfixture — fast, no subprocess. Covers success paths, theRESUME_NOT_FOUND/INVALID_PARAMSerror codes, andbatch_process's partial-failure reporting.Protocol (
test_server_speaks_mcp_protocol_over_stdio): launches the real server as a subprocess and drives it with the officialmcpclient SDK —tools/list,tools/call,resources/read— over actual JSON-RPC 2.0, so it's proving the protocol layer, not just the Python underneath it.Agent (
test_matching_agent.py): the LLM call is swapped for a deterministic fake (FakeStructuredModel) so these need no API key — they're checking the graph wiring, multi-server tool discovery, the short-circuit-on-no-new-files path, and that a second--watch-style pass only reprocesses the newly arrived file, not the whole directory.
Design decisions
MCP SDK pinned to mcp>=1.28,<2.0. The Python SDK's v2 line
shipped alongside the 2026-07-28 MCP spec revision and renames
FastMCP to MCPServer (now under mcp.server.mcpserver). v1.x is
what the current LangChain/LangGraph MCP ecosystem is built and
documented against, so this project pins to it on purpose rather than
by accident — worth revisiting once langchain-mcp-adapters and the
wider tutorial base catch up to v2.
stdio over HTTP. No network surface to secure, no auth to wire up,
and it's what MultiServerMCPClient expects for a local "command"
server. The tradeoff, confirmed while building this: each tool call
opens a fresh subprocess session rather than reusing one — fine for a
demo/CLI agent, and the honest reason a latency-sensitive production
version would move to a long-lived streamable-http server instead.
Errors are structured JSON, not prose. Every failure raises
ToolError with a JSON payload carrying a code in the JSON-RPC
"server error" range (-32000..-32099) plus a machine-readable
error label (RESUME_NOT_FOUND, DIRECTORY_NOT_FOUND,
UNSUPPORTED_FILE_TYPE, EXTRACTION_FAILED, INVALID_PARAMS).
Confirmed end-to-end against a live client session: it surfaces as
CallToolResult(isError=True, ...), and matching_agent.py's
_call_tool() re-raises it as MCPToolCallError with the code intact
instead of a caller having to string-match a message.
watch_directory polls; it doesn't push. MCP tools are
request/response, so this is a poll (a JSON state file of
filename→mtime, diffed on each call) rather than an inotify/watchdog
listener. matching_agent.py's --watch mode is what turns that into
something that feels live — a background listener pushing through MCP
resource subscriptions would be the natural next step and is
supported by the protocol, just out of scope here.
batch_process takes an explicit file list, not just a directory.
This is what lets check_new_resumes → batch_extract reprocess only
what watch_directory just reported instead of the whole folder every
time — the concurrency (asyncio.Semaphore(MAX_BATCH_CONCURRENCY)) is
what makes "efficient" in the spec actually true when that list is
long.
Env forwarding is explicit, and it's not a default you can skip.
Real gotcha hit while building this: mcp's stdio client does not
inherit the parent process's environment — it starts child servers
with a minimal default (PATH/HOME/TERM only), confirmed directly
against mcp.client.stdio.get_default_environment(). Without passing
env=dict(os.environ) explicitly in matching_agent.py's server
config, RESUME_DIRECTORY and friends silently never reach
filesystem_mcp_server.py — the agent runs, discovers tools fine, and
just quietly operates on the wrong directory. Worth knowing before it
costs you a debugging session.
Repo structure
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)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
Generate tailored, ATS-optimized resume PDFs and cover letters from a job description, over MCP.
Hosted MCP tools for FFmpeg-style video and audio processing through FFMPEG API.
Resume builder with native MCP — create and edit resumes from your AI assistant.
Public MCP server for discovering open jobs. Search, filter, and get application links.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables file system operations (read, write, list, search, watch, batch process) via MCP over JSON-RPC 2.0, used by a resume matching agent.
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with a sandboxed filesystem via MCP tools for reading, writing, searching, and monitoring files, including batch processing and resource discovery for resume management.
- FlicenseNot gradedqualityCmaintenanceProvides file system tools for resume matching agents, enabling reading, writing, searching, listing, watching, and batch processing of files via the Model Context Protocol.
- FlicenseNot gradedqualityCmaintenanceProvides MCP tools for reading, listing, writing, searching, watching, and batch-processing files, enabling automated file management and resume matching workflows.
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/GAVTIN/Resume-Matcher-MCP-Edition'
If you have feedback or need assistance with the MCP directory API, please join our Discord server