resurank-mcp
v1.0.4
Published
Local-stdio MCP server that scores how well your resume matches a job description. Runs on-device via Transformers.js — no API keys, nothing uploaded.
Maintainers
Readme
resurank-mcp
Score your resume against any job description, from inside Claude Desktop. Locally.
A Model Context Protocol server that lets Claude (or any MCP-compatible client) tell you how well your resume matches a job posting, using a hybrid 60% semantic + 40% keyword model. Everything runs on-device — no API keys, no uploads, no cloud calls. A ~25 MB embedding model is downloaded once into the standard Hugging Face cache and re-used across sessions.
Companion to the ResuRank desktop app;
they share the same scoring engine via @resurank/scoring.
Prerequisites
- Claude Desktop — download from claude.ai/download (macOS or Windows)
- Node.js v22 or later — download from nodejs.org (only needed for Option 1)
- A resume file —
.pdf,.docx,.txt, or.md; note the absolute path
One-command install (macOS / Linux)
bash <(curl -fsSL https://raw.githubusercontent.com/antonkronaj/resurank/main/packages/mcp-server/install.sh)The script checks for Node.js and Claude Desktop, prompts for your resume path, and writes the config. Pass the path as an argument to skip the prompt:
bash <(curl -fsSL https://raw.githubusercontent.com/antonkronaj/resurank/main/packages/mcp-server/install.sh) /path/to/resume.pdfThen restart Claude Desktop and you're done. Windows users see Option 1 below.
Setup
Option 1 — npx (no app required)
1. Locate the Claude Desktop config file
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
The file may not exist yet — create it if so. On macOS you can open it in one step:
open -a TextEdit ~/Library/Application\ Support/Claude/claude_desktop_config.jsonOn Windows, press Win + R, paste %APPDATA%\Claude, press Enter, and open
claude_desktop_config.json in Notepad.
2. Add the resurank server entry
If the file is empty or doesn't exist yet:
{
"mcpServers": {
"resurank": {
"command": "npx",
"args": ["-y", "resurank-mcp"],
"env": { "RESUME_PATH": "/absolute/path/to/your/resume.pdf" }
}
}
}If the file already has other MCP servers, add only the "resurank" key inside
the existing "mcpServers" object — don't replace the whole file:
{
"mcpServers": {
"some-other-server": { "...": "..." },
"resurank": {
"command": "npx",
"args": ["-y", "resurank-mcp"],
"env": { "RESUME_PATH": "/absolute/path/to/your/resume.pdf" }
}
}
}3. Set RESUME_PATH to your actual resume
Replace /absolute/path/to/your/resume.pdf with the real path. It must be
absolute (starting with / on macOS/Linux, or C:\ on Windows). Relative
paths won't work. Example:
/Users/jane/Documents/resume.pdf
C:\Users\jane\Documents\resume.pdf4. Restart Claude Desktop
Quit completely (Cmd-Q on macOS, or right-click the taskbar icon → Quit on Windows) and relaunch. Claude Desktop reads the config once at startup.
5. Verify — see Verifying it works below.
Option 2 — ResuRank desktop app (automatic)
If you have the ResuRank Electron app installed, the app writes and maintains the config for you.
1. Open ResuRank and add your resume
Paste or import your resume text in the main view. The app stores it locally.
2. Open Settings → Claude Desktop integration
Click the gear icon to open Settings, then find the Claude Desktop integration card.
3. Click Connect
ResuRank will:
- Locate your Node.js installation
- Write the
resurankentry toclaude_desktop_config.json - Export your resume to a managed file and point
RESUME_PATHat it
The card shows a ✓ Connected status when done. If the button is disabled, hover over the warning — it will tell you what's missing (Claude Desktop not found, Node.js not installed, etc.).
4. Restart Claude Desktop
Quit completely and relaunch so it picks up the new config.
Auto-sync: whenever you update your resume in ResuRank, the exported file updates automatically. No reconnecting or restarting required — the server reloads the file on the next score call.
5. Verify — see below.
Verifying it works
Open a new conversation in Claude Desktop and type:
Use the resurank tool to score my resume against a job posting.
Claude should ask you for a job title and description. If it does, the server is running. (The very first score call downloads the ~25 MB embedding model — allow 10–30 seconds depending on your connection. Subsequent calls are sub-second.)
If the tool isn't found, see Troubleshooting below.
Troubleshooting
The tool doesn't appear / Claude says it has no resume tool
Claude Desktop loads MCP tools lazily. Use a priming phrase to surface it:
"Use the resurank tool to score my resume."
If that still doesn't work, check that you restarted Claude Desktop after editing the config.
First score is very slow (30+ seconds)
Normal — the embedding model is downloading for the first time (~25 MB from Hugging Face). Progress appears in the server log. Subsequent calls are fast.
"Resume parsed to N characters (minimum 100)"
The resume file loaded but contained almost no extractable text. Common causes:
- Image-only PDF — the PDF is a scan without an OCR text layer. Convert
it to a searchable PDF first, or use the
.txtfallback. - Wrong path — double-check that
RESUME_PATHpoints to the right file. - Unsupported format — only
.pdf,.docx,.txt, and.mdare supported.
"npx: command not found" or the server fails to start
Claude Desktop couldn't find npx. Try using the full path to npx instead:
# Find the path on macOS/Linux:
which npxThen replace "npx" in the config with the full path (e.g.
/opt/homebrew/bin/npx or /usr/local/bin/npx).
Checking the server log
# macOS
tail -f ~/Library/Logs/Claude/mcp-server-resurank.logOn Windows: %APPDATA%\Claude\logs\mcp-server-resurank.log
The log shows model download progress, resume load/reload events, embed timing, and any startup errors.
Starting a scoring session
Claude Desktop loads MCP tools lazily — they aren't automatically active in every conversation. There are two ways to wake the tool up:
Option A — Use the built-in prompt (easiest)
The server exposes a prompt called "score-resume" that Claude Desktop surfaces
in its prompt picker (the + or / menu, depending on your version). Selecting it
injects a ready-made opener that tells Claude to ask you for the job posting and
call the tool immediately.
Option B — Use a priming phrase
If the prompt picker isn't visible, open a new conversation and say something like:
"Use the resurank tool to score my resume against this job posting." "Score this job description against my resume." "How well does my resume match this role?"
Any of these is enough for Claude to search its tool list and find resurank_score.
What you'll see in chat
You: Score this against my resume. Title is Senior Backend Engineer. [pastes job description]
Claude: [calls
resurank_score]Your resume scored 73.4 / 100 for this role.
- Strong semantic fit (87%) — the embedding model sees this as a very strong match conceptually.
- Matched keywords: typescript, postgres, docker, kubernetes, rest, ci/cd, …
- Missing keywords worth a look: kafka, grpc, terraform
- Critical gaps (high-weight JD terms not in your resume): kafka, grpc
The model paraphrases a summary_for_user field the server pre-computes, and is
explicitly instructed to trust the matched/missing arrays verbatim rather than
inferring what's on your resume.
Tool reference
resurank_score
Score the resume at RESUME_PATH against a job posting.
Required input
| field | type | description |
| ----------------- | ------ | --------------------------------------------------------------------------------- |
| job_title | string | Job title as the user states it. Weighted 2x in keyword scoring. |
| job_description | string | Full job description body. |
Optional input — for iterating on resume variants without restarting Claude
| field | type | description |
| ------------- | ------ | -------------------------------------------------------------------------- |
| resume_path | string | Local path to a different resume file. Bypasses RESUME_PATH for this call. |
| resume_text | string | Inline resume text. Bypasses both RESUME_PATH and any file load. |
Output (JSON)
{
"assistant_instructions": "How to report this to the user; consumed by the model.",
"resume": {
"source": "env", // "env" | "argument_path" | "inline_text"
"path": "/Users/anton/resume.pdf",
"chars": 4823,
"modified_at": "2026-06-04T17:42:18.000Z",
"preview": "Anton Kronaj — Senior Software Engineer…",
"note": "How RESUME_PATH was resolved (for the model)."
},
"score": 73.4, // 0–100
"summary_for_user": "Score: 73.4 / 100. Semantic fit: strong (87%). …",
"matched_keywords": ["typescript", "postgres", "docker", "..."],
"missing_keywords": ["kafka", "grpc", "..."],
"critical_gaps": ["kafka", "grpc"], // high-weight JD terms absent from resume
"score_breakdown": {
"semantic_score": 0.87, // embedding cosine
"keyword_score": 0.62, // TF-IDF cosine + overlap bonus
"combined_score": 0.734,
"overlap_bonus": 0.13,
"divergence_penalty": 0.0
},
"language_warning": false // true when JD looks non-English
}Configuration
| env var | required | description |
| ------------- | -------- | ---------------------------------------------------- |
| RESUME_PATH | yes | Absolute path to a .pdf, .docx, .txt, or .md |
The server stats RESUME_PATH on every call. Edit your resume on disk, save,
re-score — no restart required. The cache invalidates automatically on mtime
change.
If the resume parses to fewer than 100 characters, the server returns a clear error rather than silently scoring against an empty document. This catches image-only PDFs (no OCR) and other parse failures.
How the score works
A hybrid of two signals:
- Semantic similarity (60%) — cosine similarity between the embedding of the
resume and the embedding of the JD. Uses
Xenova/jina-embeddings-v2-small-en, a small (q8 ONNX) English embedding model that runs in Node.js via Transformers.js. Captures paraphrasing and conceptual overlap. - Keyword overlap (40%) — TF-IDF cosine plus an overlap bonus that rewards shared important terms. The JD's title is duplicated when tokenizing so it gets ~2x weight — a wrong title meaningfully skews the score.
A divergence penalty kicks in when the semantic score is high but the keyword overlap is near zero — protects against the model finding "professional-sounding text" similarity where no real keyword match exists.
A configurable critical-keyword penalty and preference-mismatch penalty
are wired in but use shipped defaults in this server; the ResuRank desktop app
exposes UI for them. See @resurank/scoring for the math.
Distribution
- npm:
resurank-mcp,npx -y resurank-mcp - Source: github.com/antonkronaj/resurank
Publishing a new version
Always use the package scripts to bump the version — do not use
npm version -w packages/mcp-server from the repo root. The workspace -w
flag ignores the package-local .npmrc and has a git-repo detection bug that
can silently fail.
1. Bump the version
# From the repo root — pick the appropriate bump:
npm -w resurank-mcp run version:patch # 1.0.2 → 1.0.3
npm -w resurank-mcp run version:minor # 1.0.2 → 1.1.0
npm -w resurank-mcp run version:major # 1.0.2 → 2.0.0This updates version in package.json only. The commit and tag must be
created manually:
git add packages/mcp-server/package.json
git commit -m "mcp-v1.0.3"
git tag mcp-v1.0.3
git push && git push --tags2. Set your npm token
export NPM_TOKEN=xxxx3. Publish
cd packages/mcp-server && npm publishprepublishOnly runs clean → build → test automatically before the publish
goes out. If any test fails, the publish is aborted.
If you also updated @resurank/scoring, publish that first — the mcp-server
depends on it and prepublishOnly installs from the registry.
Dependency version bump: resurank-mcp declares "@resurank/scoring": "^1.0.x".
The ^ range picks up all patch and minor scoring releases automatically — you do
not need to edit package.json for patch or minor scoring bumps. Only a major
scoring bump (1.x.x → 2.0.0) requires a manual dependency version update here.
Local registration (during development)
cd packages/mcp-server
npm run buildThen in claude_desktop_config.json:
{
"mcpServers": {
"resurank": {
"command": "node",
"args": ["/absolute/path/to/resurank/packages/mcp-server/dist/index.js"],
"env": { "RESUME_PATH": "/absolute/path/to/your/resume.pdf" }
}
}
}Restart Claude Desktop after editing.
License
AGPL-3.0-only. If you build a hosted service that integrates this server, the AGPL's network-clause obligations apply.
Limitations
- English embedding model only. Non-English JDs are flagged via
language_warningbut still scored using whatever overlap the multilingual fallback produces. - The embedding model is small (q8 quantized) and tuned for speed, not maximum semantic quality. For most resume/JD scoring this is the right tradeoff.
- This server uses shipped defaults for stopwords, term boosts, critical-keyword pins, and preference mismatch. The ResuRank desktop app exposes these as UI; customizing them here is not yet supported.
