Skip to main content
Glama
csarushan1729

JobScout MCP

README.md
# JobScout MCP

An MCP (Model Context Protocol) server that lets Claude search live job
postings, score them against your own skills, and track which ones
you've applied to - all running locally on your machine.

## Why this exists

Built as a hands-on project to learn how MCP servers actually work: how
Claude discovers tools, how tool schemas get defined, and how to design
tools that are genuinely useful rather than just API wrappers.

## Directory structure

```
jobscout-mcp/
├── server.py              # MCP server entrypoint - defines all 4 tools
├── scoring.py              # Scores a job posting against profile.json
├── storage.py              # SQLite-backed tracking of application status
├── profile.json             # YOUR editable skills/preferred-roles list
├── requirements.txt         # Runtime dependencies
├── requirements-dev.txt      # Adds pytest for running tests
├── .gitignore
├── LICENSE
├── README.md
└── tests/
    ├── test_scoring.py       # Unit tests for scoring.py
    └── test_storage.py       # Unit tests for storage.py
```

**How the pieces connect:** `server.py` is the only file that talks to
Claude - it imports functions from `scoring.py` and `storage.py` and
exposes them as tools. `scoring.py` reads `profile.json` (your skills)
and never touches the network or the database. `storage.py` owns the
local SQLite file (`jobscout.db`, created automatically on first run,
gitignored so it stays private) and never touches the network either.
This separation is why `tests/` can test scoring and storage logic
directly, without needing a live network connection or a running MCP
server.

## Tools exposed to Claude

| Tool | What it does |
|---|---|
| `search_jobs(keyword, limit)` | Keyword search over live postings from RemoteOK |
| `rank_jobs_for_me(keyword, limit)` | Same, but scored + sorted against your `profile.json` |
| `track_job(job_id, status, ...)` | Record or update a job's status (seen/applied/interviewing/rejected/offer/ignored) |
| `list_tracked_jobs(status)` | List what you're tracking, optionally filtered by status |

## Setup

```bash
git clone <your-repo-url>
cd jobscout-mcp
python3 -m venv venv
source venv/bin/activate       # on Windows: venv\Scripts\activate
pip install -r requirements.txt
```

Edit `profile.json` with your actual skills and preferred role keywords
before using `rank_jobs_for_me` - the scoring is only as good as this list.

## Running the tests

```bash
pip install -r requirements-dev.txt
pytest tests/ -v
```

All storage tests run against temporary throwaway databases (via
pytest's `tmp_path` fixture), so they never touch your real
`jobscout.db`.

## Quick manual test (no Claude needed)

```bash
python3 -c "
import asyncio
from server import search_jobs, rank_jobs_for_me
print(asyncio.run(search_jobs('python', limit=5)))
print(asyncio.run(rank_jobs_for_me(limit=5)))
"
```

```bash
python3 -c "
from server import track_job, list_tracked_jobs
print(track_job('abc123', 'applied', title='Backend Engineer', company='Acme'))
print(list_tracked_jobs())
"
```

These hit the live RemoteOK API and a real local SQLite file, so you
need actual internet access and a writable directory for this to work.

## Connecting to Claude Desktop

Add this to your Claude Desktop config
(`~/Library/Application Support/Claude/claude_desktop_config.json` on Mac,
`%APPDATA%\Claude\claude_desktop_config.json` on Windows):

```json
{
  "mcpServers": {
    "jobscout": {
      "command": "python3",
      "args": ["/absolute/path/to/jobscout-mcp/server.py"]
    }
  }
}
```

Restart Claude Desktop, then try:
- "Use jobscout to find remote backend Python jobs."
- "Rank the live postings against my skills profile."
- "Mark job [id] as applied, I just submitted it."
- "What jobs am I currently tracking as interviewing?"

## Known limitations

- Only pulls from RemoteOK's public API - a real production version
  would pull from multiple job boards.
- Scoring is keyword-based, not semantic - it won't know that "Golang"
  means the same thing as "go" unless both are in `profile.json`.
- No automated monitoring of scoring quality over time; see
  `tests/test_scoring.py` for what's covered today.

## Roadmap (built in this order)

- [x] Part 1: live job search tool
- [x] Part 2: score/rank postings against your own skills
- [x] Part 3: local storage to track seen/applied jobs
- [x] Part 4: tests, docs, packaging