Skip to main content
Glama
jpita

job-search-mcp

by jpita
README.md
# job-search-mcp

Personal MCP server (stdio) that aggregates job listings from **Remotive** and **Arbeitnow**, with local SQLite caching, application tracking, and saved searches.

## Setup

```bash
npm install
npm run build        # emits dist/
npm test             # 27 tests
npm run typecheck
```

Requires Node 20+. The cache DB lives at `~/.job-search-mcp/jobs.db` (override with `JOB_SEARCH_MCP_DB`).

## Environment variables

No API keys needed — both providers are public.

| Var | Needed for |
|---|---|
| `JOB_SEARCH_MCP_DB` | optional DB path override |

## Register in Claude Desktop

`claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "job-search": {
      "command": "node",
      "args": ["/absolute/path/to/job-search-mcp/dist/index.js"]
    }
  }
}
```

## Register in Claude Code

```bash
claude mcp add job-search \
  -- node /absolute/path/to/job-search-mcp/dist/index.js
```

## Tools & example prompts

| Tool | Example prompt |
|---|---|
| `search_jobs` | "Find remote QA automation jobs with Playwright posted in the last week" |
| `get_job_details` | "Show me the full description of that Clipster job" |
| `whats_new` | "Anything new since yesterday matching playwright?" |
| `track_job` | "Mark that job as applied — note: sent CV via referral" |
| `list_tracked` | "Show my application pipeline" / "What am I interviewing for?" |
| `save_search` / `run_saved_search` / `list_saved_searches` | "Save this as my daily qa-remote search" → "Run my qa-remote search" |
| `set_profile` / `get_profile` | Paste your CV (or attach the file) → "store this as my profile" → the model derives target titles/skills and proposes searches; later: "search based on my CV" |

Job ids look like `remotive:2091062` / `arbeitnow:<slug>` and appear in every search result block.

## Smoke test

`node smoke.mjs` spins up the built server over stdio and exercises every tool against the live APIs (uses a throwaway DB at `/tmp/job-search-smoke.db`).

## Provider quirks discovered during development

- **Remotive** (`https://remotive.com/api/remote-jobs`): endpoint as documented. `publication_date` has **no timezone suffix** (treated as UTC). `salary` is a free-text string — parsed best-effort into min/max/currency. Descriptions are heavy HTML (Tailwind inline styles) — stripped and truncated to ~1500 chars. Remotive asks not to poll aggressively, so responses are cached in-memory for 15 minutes per query on top of the SQLite cache.
- **Arbeitnow** (`https://www.arbeitnow.com/api/job-board-api`): endpoint as documented; `remote` and `visa_sponsorship` params work. **Undocumented**: a `search` query param is honored by the API (visible in its pagination links) — used here, plus a client-side all-terms filter as a safety net since server-side matching is loose. `created_at` is a unix timestamp. Heavily German-market (many listings in German). Paginated via `links.next`; the adapter fetches up to 3 pages.

Discrepancies vs. the original spec: none for endpoints — both matched their docs. The only additions are the undocumented Arbeitnow `search` param (used) and the Remotive no-timezone date quirk (handled).