io.github.michalper/mcp-job-search

السجل الرسمي أدوات المطورين

Job search mechanics for an AI assistant: search, fetch postings, render CVs, lint, log.

التثبيت

أضف هذا إلى إعدادات MCP في عميلك (Claude Desktop: claude_desktop_config.json، وCursor: ‏.cursor/mcp.json، ويستخدم VS Code وغيره الصيغة نفسها «mcpServers»).

PYPI package mcp-job-search-mp
Claude Code
claude mcp add mcp-job-search -- uvx mcp-job-search-mp==0.2.0
إعداد JSON (Claude Desktop وCursor وVS Code)
{
    "mcpServers": {
        "mcp-job-search": {
            "command": "uvx",
            "args": [
                "mcp-job-search-mp==0.2.0"
            ]
        }
    }
}

التوثيق

مأخوذ من ملف README للمشروع على GitHub ويُحدَّث أسبوعيًا. تفتح الروابط على GitHub.

المحتويات
  1. mcp-job-search
  2. How this differs from other job-search MCP servers
  3. Install
  4. Connecting to Claude Code
  5. Example: catching a CV the way lint_cv sees it
  6. Tools
  7. Verified job-board coverage (fetch_job_posting, 2026-10-19)
  8. Why a configurable linter, not a hardcoded one
  9. Development
  10. License

An MCP server that automates the repetitive mechanics around job-hunting with an AI coding assistant, while deliberately not automating the parts that need a human: drafting your CV/cover letter, fact-checking claims against your real history, and submitting an application.

It answers the question that keeps eating a job search's time: "this CV draft looks fine, but did it quietly fabricate something, and is this one specific posting even worth reading?"

Status: early stage. Built for personal use, published because the configurable anti-fabrication linter and application-registry tooling are generically useful beyond one person's job search. All seven tools are working. See CHANGELOG.md for what changed and what was verified live.

How this differs from other job-search MCP servers

A few open-source MCP servers already do job search + CV tailoring (not-boring-resume-mcp, resume-mcp, Workopia MCP, IAM MCP Server, LinkedIn MCP Server, among others). This one targets what they don't:

  1. It won't scrape past a bot-detection wall, even quietly. NoFluffJobs, JustJoin.it and similar Polish IT boards have no public search API — fetch_job_posting reads one URL you already found instead of indexing their listings, and on boards confirmed to sit behind an active Cloudflare challenge (Pracuj.pl, Indeed), it fails loudly rather than trying to solve the challenge automatically. See the verified coverage table below — this is a documented boundary, not a silent gap.
  2. The anti-fabrication linter is a configurable rules engine, not hardcoded prose. None of the surveyed alternatives ship a way to enforce your own banned-phrase list, achievement-weight rules, or required clauses — lint_cv reads them from a YAML file you control, so the mechanism is reusable even though the facts it checks against are personal (see "Why a configurable linter" below).
  3. It doesn't draft or fact-check for you, on purpose. Writing the CV/cover-letter text and adversarially verifying every claim traces back to something true stays with whatever AI assistant calls these tools — this server is a mechanics layer (search, fetch, render, lint, log), not a weaker bundled LLM call duplicating judgment the calling assistant already does better with full context.

What this server doesn't pretend to be: it doesn't cover every job board — only ones with a real API, or a single posting URL a human already found. It never submits anything on your behalf. And a clean lint_cv run doesn't mean a CV is honest, only that it passes the specific rules you wrote — semantic fabrication (two true facts combined into a false-sounding one) still needs a human or an independent adversarial review to catch.

Install

From PyPI (published as mcp-job-search-mp — mcp-job-search was already taken by an unrelated project; the command stays mcp-job-search):

pip install mcp-job-search-mp
# or, for an isolated tool install:
uvx mcp-job-search-mp

From source, for development:

git clone https://github.com/michalper/mcp-job-search.git
cd mcp-job-search
python3.12 -m venv .venv
.venv/bin/pip install -e ".[dev]"

Connecting to Claude Code

{
  "mcpServers": {
    "job-search": {
      "command": "/absolute/path/to/mcp-job-search/.venv/bin/mcp-job-search",
      "env": {
        "LINT_RULES_PATH": "/absolute/path/to/your/lint_rules.yaml",
        "REGISTRY_CONFIG_PATH": "/absolute/path/to/your/registry.yaml"
      }
    }
  }
}

Configure via environment variables — all optional, every tool degrades gracefully when something is unset:

VariableUsed byDefault
ADZUNA_APP_ID, ADZUNA_APP_KEYsearch_jobsunset — Adzuna skipped
ADZUNA_COUNTRYsearch_jobspl
RAPIDAPI_JSEARCH_KEYsearch_jobsunset — JSearch skipped
JSEARCH_COUNTRYsearch_jobsus (JSearch's own API default)
LINT_RULES_PATHlint_cvbundled src/mcp_job_search/examples/lint_rules.example.yaml
REGISTRY_CONFIG_PATHlog_applicationbundled src/mcp_job_search/examples/registry.example.yaml
CHROME_PATHrender_pdfauto-detected (macOS app bundle, Linux $PATH, common Windows install dirs)
SEARCH_CACHE_DIRsearch_jobs~/.cache/mcp-job-search/search
SEARCH_CACHE_TTL_SECONDSsearch_jobs21600 (6 hours)
Adzuna and JSearch free-tier limits

Adzuna's free tier is 25 one-time test calls, then a paid plan (£8/month, 5,000 calls) — budget calls accordingly once past testing. JSearch's free tier is 200 requests/month; search_jobs caches results by query (SEARCH_CACHE_DIR/SEARCH_CACHE_TTL_SECONDS, default 6h) specifically to protect that quota from repeated identical queries — pass use_cache=False to force a fresh one when you actually need it.

Sites behind active bot-detection

fetch_job_posting will never try to solve a CAPTCHA or bot-detection challenge automatically, on any site, regardless of how narrow the request — confirmed via response headers that Pracuj.pl and Indeed (pl.indeed.com) both sit behind an active Cloudflare challenge (cf-mitigated: challenge / 401 with a Cloudflare bot-management cookie), not just a missing-JavaScript issue. A real, human-driven browser session passes both normally — for a page like this, read it with a real browser instead of this tool.

Example: catching a CV the way lint_cv sees it

The CV draft here (tests/fixtures/sample_cv_with_violations.html) was seeded on purpose, and lint_cv is pointed at the bundled generic example rules (src/mcp_job_search/examples/lint_rules.example.yaml), not a real person's facts:

{
  "clean": false,
  "findings": [
    {"severity": "error", "message": "generic corporate jargon — replace with a concrete claim"},
    {"severity": "error", "message": "unsupported superlative — ground it in a specific result or cut it"},
    {"severity": "error", "message": "example: list cloud/K8s here only if you have real production experience"},
    {"severity": "error", "message": "a .gitignore edit is hygiene, not an achievement"},
    {"severity": "error", "message": "a commit count is not an achievement on its own"},
    {"severity": "error", "message": "example: flag a title claim that contradicts your real specialization"},
    {"severity": "note", "message": "link rendered as <a href> in body (https://example.com/portfolio) — use plain text"}
  ]
}

clean is false because real rules fired, not a guess — and every message came from the YAML file, not from code that knows anything about a specific person's career.

Tools

ToolStatusDescription
search_jobsworkingreal search across Arbeitnow (free, no key), Adzuna and JSearch (API keys) — verified live against the Polish PHP market on both paid sources (10 real listings each, recognizable employers); results cached to protect JSearch's free quota
fetch_job_postingworkingsingle-URL extraction via trafilatura — verified live, clean extraction on JustJoin.it, NoFluffJobs, Bulldogjob, theprotocol.it, Solid.Jobs, CzyJestEldorado.pl, and LinkedIn single-posting pages; confirmed, by design, not to bypass Pracuj.pl/Indeed's Cloudflare bot-detection challenge
render_pdfworkingheadless-Chrome HTML→PDF with the exact flags that avoid known hangs/silent failures; cross-platform CHROME_PATH auto-detection; page-count verification via raw PDF byte scan
lint_cvworkingconfigurable mechanical linter — banned phrases, skills-section-only bans, achievement-weight overclaims, title/GDPR/link checks, page-count limits, all driven by a YAML file you control
log_applicationworkingappends one row to a Markdown application registry in the correct section and date position, with a column_map for non-English headers; never invents free-text content
list_applicationsworkingread-only query over the registry (filter by portal/company) — use before log_application to check for a duplicate
update_application_statusworkingrewrites one row's status cell in place; refuses to guess when more than one row matches

Verified job-board coverage (fetch_job_posting, 2026-10-19)

SiteResult
JustJoin.it✅ clean extraction
NoFluffJobs✅ clean extraction
Bulldogjob✅ clean extraction
theprotocol.it✅ clean extraction
Solid.Jobs✅ clean extraction
CzyJestEldorado.pl (aggregator)✅ clean extraction
LinkedIn (/jobs/view/<id>)✅ clean extraction — the single-posting view is public
JoobleN/A — redirects to the source site (e.g. JustJoin.it); nothing of its own to fetch
Pracuj.pl❌ blocked — active Cloudflare challenge, not attempted
Indeed (pl.indeed.com)❌ blocked — active Cloudflare challenge, not attempted

Why a configurable linter, not a hardcoded one

The original motivation: a CV draft once conflated two separate true facts into one false-sounding claim, and it shipped before anyone caught it. A grep-based mechanical check can catch a banned phrase; it can't catch semantic conflation — that still needs a human or an independent adversarial review. What a mechanical linter can do reliably: enforce your own banned-phrase list, page limits, and required clauses, consistently, every time, without you re-checking the same things by hand. lint_cv is that — generic, so anyone can point it at their own facts.

Development

.venv/bin/python -m pytest --cov     # tests + coverage
.venv/bin/ruff check .               # lint
.venv/bin/mypy src                   # types
.venv/bin/bandit -c pyproject.toml -r src   # security
.venv/bin/pip-audit                  # dependency vulnerabilities

Run .venv/bin/pre-commit install once to run all of the above (except pip-audit, which needs network access and stays CI-only) on every commit.

One manual end-to-end check (not run in CI — needs live network and a real Chrome install), also documented in CHANGELOG.md:

  1. search_jobs(keywords="PHP", remote_only=True, sources=["arbeitnow"]) against the live Arbeitnow API.
  2. fetch_job_posting(url=...) on a URL from step 1.
  3. render_pdf(html_path=...) on a sample CV, confirm success and page_count.
  4. lint_cv(html_path=...) on a file with a seeded violation, confirm it's reported.
  5. log_application(..., dry_run=True) against your own registry, confirm the previewed row and insertion point look right before dropping dry_run.

See CHANGELOG.md for what changed in each release.

License

MIT — see LICENSE.

الأسئلة الشائعة

ما هو io.github.michalper/mcp-job-search؟

Job search mechanics for an AI assistant: search, fetch postings, render CVs, lint, log.

كيف أثبّت io.github.michalper/mcp-job-search؟

يعمل على جهازك. في Claude Code: claude mcp add mcp-job-search -- uvx mcp-job-search-mp==0.2.0. وفي العملاء الآخرين، الصق إعداد JSON من هذه الصفحة في إعدادات MCP الخاصة بهم.

هل يحتاج io.github.michalper/mcp-job-search إلى مفتاح API؟

لا يذكر إدراجه في السجل أي مفتاح أو سر إلزامي. قد تتطلب بعض الميزات حسابًا لدى الخدمة التي يتصل بها.

هل io.github.michalper/mcp-job-search مفتوح المصدر؟

نعم. شيفرته متاحة على GitHub بترخيص MIT.

من يصون io.github.michalper/mcp-job-search؟

ينشره michalper. يدرجه AI.tn لكنه لا يصونه ولا يوصي به.

من سجل MCP الرسمي. الإدراج ليس توصية: راجع شيفرة الخادم وصلاحياته قبل منحه الوصول إلى بياناتك.