Resume Matcher MCP Server
Provides sandboxed file system tools (read, write, search, list, watch) over the Model Context Protocol, enabling resume matching and analysis workflows via an agent.
README
Resume Matcher — MCP Server & LangGraph Agent (Milestone 4)
A resume-matching system whose file-system tooling is served over the Model Context Protocol (MCP). The custom file tools from Milestone 1 are now a standalone, JSON-RPC 2.0-compliant MCP server; the Milestone 3 LangGraph conversational agent consumes them through an MCP client facade — same functionality, standardized architecture. A second, optional MCP server (DuckDuckGo, stdio) adds web search as a bonus.
Architecture
The system is split across a protocol boundary: the agent process never touches the filesystem directly at runtime — every read/write crosses JSON-RPC 2.0.
graph LR
subgraph Host["Agent process (agent_cli.py / app.py)"]
CLI[agent_cli.py<br/>flags: --mcp-url, --local-fs,<br/>--require-mcp, --web-search]
AG[MatchingAgent<br/>LangGraph StateGraph]
ENG[Engine<br/>JobMatcher + LLMClient<br/>+ fs: FileStore + web]
FS[FileStore protocol<br/>mcp_fs_client.py]
LOCAL[LocalFileStore<br/>delegates to fs_tools]
MCPC[McpFileStore<br/>sync bridge over asyncio]
DDGC[McpWebSearch<br/>stdio client]
CLI --> AG --> ENG --> FS
FS -.impl.-> LOCAL
FS -.impl.-> MCPC
ENG -.optional.-> DDGC
end
subgraph Server["filesystem_mcp_server.py (FastMCP)"]
EP["/mcp — Streamable HTTP<br/>JSON-RPC 2.0"]
HEALTH["/health — GET"]
TOOLS["6 tools: read_file, list_files,<br/>write_file, search_in_file,<br/>watch_directory, batch_process"]
RES["resources: resume://{filename},<br/>resume://index"]
SANDBOX[fs_tools sandbox<br/>FS_TOOLS_BASE_DIR]
EP --> TOOLS --> SANDBOX
EP --> RES --> SANDBOX
end
DDG[duckduckgo-mcp-server<br/>subprocess]
MCPC -- "HTTP POST /mcp" --> EP
DDGC -- "stdio JSON-RPC" --> DDG
LOCAL -- "direct calls (offline/test)" --> SANDBOX2[fs_tools sandbox]
Components
| Component | File | Role |
|---|---|---|
| MCP file-system server | filesystem_mcp_server.py |
FastMCP (official SDK), Streamable HTTP at /mcp, GET /health. Wraps the sandboxed M1 fs_tools primitives — it does not reimplement them. |
| M1 tool layer | fs_tools.py |
Sandboxed file primitives: path-escape rejection, .txt/.pdf/.docx text extraction. |
| Client facade | mcp_fs_client.py |
FileStore protocol; LocalFileStore (offline/tests), McpFileStore (sync bridge over Streamable HTTP), McpWebSearch (DuckDuckGo MCP over stdio). |
| Matching agent | matching_agent.py |
LangGraph HITL agent (M3 topology unchanged); all runtime file I/O goes through the injected FileStore — no direct fs_tools import. |
| Entry points | agent_cli.py, app.py |
CLI (--mcp-url, --local-fs, --require-mcp, --web-search) and Streamlit UI, both with graceful MCP fallback. |
| RAG + matcher | resume_rag.py, job_matcher.py, reranker.py |
ChromaDB retrieval, ranking, reranking (M2). |
The two MCP servers
1. Filesystem server (filesystem_mcp_server.py) — Streamable HTTP
Tools (JSON Schemas auto-generated by FastMCP from type hints, discovered
via tools/list):
| Tool | What it does |
|---|---|
read_file |
Read a file inside the sandbox; extracts text from .txt/.pdf/.docx. |
list_files |
List directory contents with metadata. |
write_file |
Write content; returns bytes_written. |
search_in_file |
Keyword search with char offsets + snippets per match. |
watch_directory |
Stateful poll cursor: first call baselines, later polls with the same watch_id report new / modified / deleted files. |
batch_process |
Concurrent multi-file ops with per-file failure isolation — one bad path never fails the batch. |
Resources: resume://index plus one resume://{filename} per file in
resumes/ — the resource list tracks the live directory, so adding or removing
a resume changes what resources/list returns.
Session lifecycle (standard MCP over JSON-RPC 2.0):
initialize → notifications/initialized → tools/list → tools/call …
→ resources/list → resources/read …
2. Web-search server (bonus) — duckduckgo-mcp-server over stdio
Spawned as a per-session subprocess by McpWebSearch; no API key needed.
Used by the agent's deep-screen step for market context, wrapped in
try/except — if search fails, the analysis completes without it.
Why two transports?
Streamable HTTP for the file server: it's a long-lived, independently
addressable service — multiple clients can share it, and it gets a real ops
surface (GET /health, curl-able). stdio for the search server: a per-session
helper with no reuse requirement, so spawning it as a child process is the
simpler fit. Details in docs/architecture.md.
Error model (three tiers)
| Tier | Example | Surface |
|---|---|---|
| Domain outcome | File not found, path escapes sandbox | {"success": false, "error": ...} in a normal result — the agent branches on it as data, never an exception |
| Programmer error | Invalid operation enum |
ValueError → isError: true — loud, signals a caller bug |
| Protocol error | Malformed JSON-RPC | JSON-RPC error object, handled by the MCP SDK |
Sync bridge
LangGraph nodes are synchronous; the MCP SDK is async. McpFileStore runs a
daemon thread hosting a private asyncio loop, and a single coroutine owns the
open/close of the HTTP session (anyio cancel scopes must stay on one task).
Sync callers submit via run_coroutine_threadsafe with a 30 s timeout, so a
hung server can never deadlock a graph node.
Agent workflow — where MCP is called
The LangGraph pipeline (parse_jd → extract_requirements → search_resumes → rank_candidates → summarize_shortlist → generate_report → human_feedback)
loops on a HITL interrupt (refine / compare / interview / screen /
explain / chat / done). Two states cross the MCP boundary:
deep_analyze(fan-out per shortlisted candidate onscreen) —tools/call read_fileto fetch the full resume body, plus optional DDG search for market context.write_decision_log(ondone) —tools/call write_fileto persist the decision log JSON.
Retrieval/ranking use the local ChromaDB index directly: RAG indexing is an
offline batch pipeline, and routing thousands of embedding reads through a
network protocol adds latency for zero interoperability benefit
(see the refactor boundary in docs/architecture.md).
Sequence + state-machine diagrams:
docs/diagrams/agent_mcp_interaction.md.
Resilience
- Graceful fallback: the CLI and Streamlit UI probe the server at startup;
if unreachable they warn and fall back to
LocalFileStore(identical interface, no network). --require-mcp: hard-fails with a start hint when the server is down — for demos that must prove MCP is in the loop.- Sandbox: every tool and resource resolves paths against
FS_TOOLS_BASE_DIR; escape attempts get a structured error. Server binds to loopback by default. - Known gaps (out of scope): no auth/TLS on
/mcp, single-process server, watch cursors are in-memory.
Setup
python -m venv --system-site-packages .venv
.venv\Scripts\pip install -r requirements.txt
copy .env.example .env # add ANTHROPIC_API_KEY for live LLM runs
Run
# Terminal 1 — MCP server
.venv\Scripts\python filesystem_mcp_server.py
# health check: http://127.0.0.1:8765/health
# Terminal 2 — agent through MCP
.venv\Scripts\python agent_cli.py "senior python backend engineer" --require-mcp
# optional bonus: add --web-search (DuckDuckGo MCP, no API key)
# Streamlit UI (connects to MCP automatically; web search is a sidebar toggle)
.venv\Scripts\python -m streamlit run app.py
Configuration precedence is CLI > env > default for base dir
(FS_TOOLS_BASE_DIR), host/port (MCP_FS_HOST/MCP_FS_PORT), and the
client URL (MCP_FS_URL, default http://127.0.0.1:8765/mcp).
Demo & tests
# Narrated end-to-end demo (server + discovery + all 6 tools + watch + batch + agent).
# Asserts every step's expectation — exits 0 only if all hold.
.venv\Scripts\python scripts\demo_m4.py # full
.venv\Scripts\python scripts\demo_m4.py --skip-agent # tools only, no API key needed
# Test suite (fully offline — StubLLM, no API calls). Integration tests start a
# real server subprocess and speak real Streamable HTTP — no mocked transport.
.venv\Scripts\python -m pytest -q
Scenario-by-scenario coverage map (each deliverable → the test or demo step
that proves it): docs/test_scenarios.md.
Docs
docs/problem_statement.md— assignment framingdocs/architecture.md— full design: transport choice, JSON-RPC flow, error model, config, securitydocs/features.md— feature inventorydocs/diagrams/agent_mcp_interaction.md— sequence + LangGraph state machine with MCP call sitesdocs/test_scenarios.md— S1–S12 scenario table mapped to tests/demo steps
Earlier milestones (M1 file tools, M2 RAG + job matcher, M3 LangGraph agent) are included as the working baseline this milestone builds on.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.