Skip to main content
Glama
galmrt

job-search

by galmrt

job-search MCP server

A local MCP server that scrapes job boards (Indeed, LinkedIn, ZipRecruiter, Google Jobs — via python-jobspy), filters postings against your saved criteria, dedupes against what you've already been shown, and tracks application status. Runs on your machine, registered with Claude Desktop.

1. Install dependencies

This has to be done in a real Terminal on your Mac (Cowork's sandboxed shell can't reach PyPI, so I couldn't run this step for you). One time:

cd ~/Projects/job-search-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

That installs mcp, python-jobspy, and pandas into a virtualenv scoped to this folder.

Related MCP server: Whoishiring MCP Server

2. Smoke-test it

cd ~/Projects/job-search-mcp
.venv/bin/python -c "import server; print('imports ok')"

If that prints imports ok with no traceback, the code is wired up correctly. (It won't try to scrape anything yet — that only happens when a tool is actually called.)

Optional: try a real scrape from the command line before wiring it into Claude Desktop:

.venv/bin/python -c "
from scraper import run_search
jobs = run_search('machine learning engineer', 'Remote', ['indeed'], hours_old=72, results_wanted=5)
for j in jobs:
    print(j.get('title'), '-', j.get('company'), '-', j.get('job_url'))
"

3. Register with Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (create it if it doesn't exist) and add an entry under mcpServers:

{
  "mcpServers": {
    "job-search": {
      "command": "/Users/mrtgaliakberov/Projects/job-search-mcp/.venv/bin/python",
      "args": ["/Users/mrtgaliakberov/Projects/job-search-mcp/server.py"]
    }
  }
}

If you already have other mcpServers entries, just add "job-search" as another key alongside them — don't replace the whole file.

Then fully quit and reopen Claude Desktop. The server starts automatically whenever Claude Desktop launches it (you don't need to run it manually).

4. Try it

In a Claude Desktop chat:

  • "What are my saved job search criteria?" → calls list_criteria

  • "Get today's job matches" → calls get_daily_matches

  • "Search for AI engineer jobs in Seattle" → calls search_jobs

  • "Mark as applied" → calls mark_applied

  • "What have I marked as applied so far?" → calls list_tracked

5a. Extra sources: ZipRecruiter, Dice, Indeed (connectors)

Beyond Indeed/LinkedIn (JobSpy), you can pull in results from Claude's native ZipRecruiter, Dice, and Indeed connectors if you've connected them (claude.ai connector settings). They hit real provider APIs, not scraping -- no blocking, no ToS risk.

They're a different mechanism than the job-search MCP server: Claude calls the connector's search_jobs tool itself, then passes the raw response to one of this server's record_zip_recruiter_matches / record_dice_matches / record_indeed_matches tools, which normalizes it and runs it through the same filter/dedupe/store pipeline get_daily_matches uses -- same seen_jobs table, same dashboard, same application tracker. Just ask in chat, e.g.:

"Search ZipRecruiter for machine learning engineer jobs, remote, entry-level, and record the matches"

ZipRecruiter's connector has real seniority_classes (NO_EXPERIENCE/JUNIOR/MID/SENIOR) and location_types (REMOTE/HYBRID) filters -- worth leaning on those directly rather than relying on this project's title-keyword filtering, since they're much more precise. Dice and Indeed's connectors don't have that, so the usual title_exclude_keywords filtering still applies to their results.

The daily scheduled task also tries all three connectors automatically if they're available in that session (see CLAUDE.md for the current status of that -- it's unverified as of when this was written).

5. Browse matches in a dashboard

Once you've run get_daily_matches at least once, browse everything it's found so far in a local web page:

python3 dashboard.py

Opens http://127.0.0.1:8765 in your browser automatically. Search, filter by status or remote-only, sort any column, and change a job's status (interested / applied / interviewing / rejected / skipped) right from the table -- it writes to the same state.db mark_applied uses, so status stays in sync whichever way you set it.

No install needed for this part -- it's stdlib only, doesn't touch .venv. --port <n> to use a different port, --no-browser to skip auto-opening.

Tools

Tool

Purpose

search_jobs

Ad-hoc search, no dedupe/state side effects

get_daily_matches

Full saved-criteria search → filter → dedupe → mark seen. This is what the daily scheduled check calls.

list_criteria / update_criteria / reset_criteria

View/edit what counts as a match

mark_applied / list_tracked

Lightweight application tracker

get_stats

Counts: jobs seen, applications tracked, by status

Default criteria

  • Titles matched: anything containing an AI/ML term (machine learning, AI engineer, applied AI/ML, research engineer, etc. — any seniority), OR a general SWE term (software engineer, developer) AND an entry-level signal (new grad, junior, entry-level, associate, early career).

  • Excluded: senior/staff/principal/lead/director/manager/architect titles, regardless of the above.

  • Search terms: "machine learning engineer", "AI engineer", "software engineer new grad"

  • Locations: "Remote", "San Jose, CA" — no hard location filter beyond that (I left this broad since you said Remote / Bay Area / anywhere in the US all worked for you); edit locations via update_criteria to narrow it.

  • Sites: Indeed, LinkedIn, ZipRecruiter, Google Jobs, Glassdoor. In practice, only Indeed and LinkedIn actually return results right now — ZipRecruiter, Google, and Glassdoor all come back empty (no error, just 0 results) due to upstream JobSpy scraper issues (see GitHub #302 for ZipRecruiter/Google; Glassdoor tested the same way with the same result). No free fix exists — the workaround JobSpy documents is a paid rotating-residential-proxy subscription. They're left in sites in case JobSpy fixes this upstream (nothing to configure if it does).

  • Non-US "Remote" listings are filtered out. LinkedIn's "Remote" search isn't country-scoped and returns postings from anywhere in the world — filters.py's is_non_us_location() drops anything whose location names a non-US country.

  • Window: last 48 hours of postings, dedup'd against everything previously shown (stored in ~/.job-search-mcp/state.db).

All of this is editable at runtime with update_criteria — no code changes or restart needed.

A note on LinkedIn/Indeed scraping

JobSpy scrapes these sites directly (no official API), which is against their terms of service. It mimics ordinary browser traffic rather than doing anything like credential stuffing or session hijacking, and this server is meant to run about once a day, not be polled — but there's still some risk of the scraper breaking or getting rate-limited if these sites change their defenses. If you'd rather drop LinkedIn/Indeed entirely, remove them from sites via update_criteria and keep ZipRecruiter/Google/Indeed... (well, keep whichever subset you're comfortable with).

Data

Everything is stored locally in ~/.job-search-mcp/state.db (SQLite) — seen-job history, your criteria, and your application tracker. Nothing leaves your machine except the scrape requests themselves.

Maintenance

ActivityMaintained
ResponsivenessNo issues

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

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables job search and scraping across multiple job boards (LinkedIn, Indeed, Glassdoor, etc.) with advanced filtering, directly from Claude Desktop or other MCP clients.
    5
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables Claude Desktop to scan, analyze, and track job postings from Hacker News and Y Combinator's Work at a Startup using a 24-tool MCP server with multi-stage filtering and resume-based ranking.
    -
  • A
    license
    A
    quality
    B
    maintenance
    A personal job-search assistant for Claude Desktop that searches real job boards, scores each job 0–100 for fit, and displays a ranked board for fast triage.
    10
    293
    3
    MIT
  • F
    license
    B
    quality
    C
    maintenance
    Enables searching justjoin.it for job listings and tracking applications locally with status updates, all via natural language in Claude.
    4
    -