Skip to main content
Glama
bendza

Jobless MCP

by bendza
README.md
[![Install in Cursor](https://img.shields.io/badge/Install_in-Cursor-000000?style=flat-square&logoColor=white)](https://cursor.com/en/install-mcp?name=jobless&config=eyJ1cmwiOiAiaHR0cHM6Ly9tY3Auam9ibGVzcy5kZXYvbWNwIn0%3D)
[![Install in VS Code](https://img.shields.io/badge/Install_in-VS_Code-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=jobless&config=%7B%22type%22%3A%20%22http%22%2C%20%22url%22%3A%20%22https%3A//mcp.jobless.dev/mcp%22%7D)

# Jobless MCP

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![CI](https://github.com/bendza/jobless-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/bendza/jobless-mcp/actions/workflows/ci.yml)

> Personalized job search inside Claude. Get ranked job matches based on your actual skills, not keywords. Works in Claude Code, Desktop, Cursor, Windsurf, VS Code, Gemini CLI, and more.

**[Get your API key](https://jobless.dev/mcp)** | **[Documentation](https://jobless.dev/mcp)** | **[Issues](https://github.com/bendza/jobless-mcp/issues)**

## Why Jobless MCP?

### Without Jobless

- Searching job boards manually with keyword filters
- Getting irrelevant results because "Python" matches a snake handler job
- No idea how well you actually match a role until you read the whole posting
- Context-switching between your AI assistant and browser tabs

### With Jobless

Ask Claude naturally and get jobs ranked against your actual resume:

```
What are my best job matches today?
```

Claude calls `get_best_matches` and returns:

```
Top 5 matches personalized to your resume:

1. [94%] Staff AI Engineer - Anthropic
   Remote | Python, PyTorch, Distributed Systems
   role 92% | skills 95% | seniority 96%

2. [91%] Senior ML Engineer - Vercel
   Remote | Go, Postgres, Kubernetes, LLM infra
   role 88% | skills 93% | seniority 91%

3. [89%] Founding Engineer - Cursor
   San Francisco | TypeScript, React, LLMs
   role 92% | skills 88% | seniority 87%
```

10,000+ jobs indexed across Greenhouse, Ashby, Lever, and more. Updated daily.

## Quick Start

### 1. Get your API key

Visit **[jobless.dev/mcp](https://jobless.dev/mcp)** -- drop your resume and get an API key instantly (no signup required for 10 matches/day).

### 2. Connect your client

Remote server URL:

```
https://mcp.jobless.dev/mcp
```

<details>
<summary><b>Claude Code</b></summary>

```bash
claude mcp add jobless --url https://mcp.jobless.dev/mcp --header "Authorization: Bearer YOUR_API_KEY"
```

</details>

<details>
<summary><b>Claude Desktop</b></summary>

Jobless is available as a Claude Connector. Go to **Settings > Connectors** and search for **Jobless**.

Or add manually to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "jobless": {
      "url": "https://mcp.jobless.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
```

Restart Claude Desktop after saving.

</details>

<details>
<summary><b>Cursor</b></summary>

Add to `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "jobless": {
      "url": "https://mcp.jobless.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
```

</details>

<details>
<summary><b>VS Code</b></summary>

Add to `.vscode/mcp.json`:

```json
{
  "servers": {
    "jobless": {
      "type": "http",
      "url": "https://mcp.jobless.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
```

</details>

<details>
<summary><b>Windsurf</b></summary>

Add to `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "jobless": {
      "serverUrl": "https://mcp.jobless.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
```

</details>

<details>
<summary><b>Zed</b></summary>

Add to Zed settings:

```json
{
  "context_servers": {
    "jobless": {
      "url": "https://mcp.jobless.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
```

</details>

<details>
<summary><b>Gemini CLI</b></summary>

Add to `~/.gemini/settings.json`:

```json
{
  "mcpServers": {
    "jobless": {
      "httpUrl": "https://mcp.jobless.dev/mcp"
    }
  }
}
```

</details>

<details>
<summary><b>OpenCode</b></summary>

Add to `opencode.json`:

```json
{
  "mcp": {
    "jobless": {
      "type": "remote",
      "url": "https://mcp.jobless.dev/mcp",
      "enabled": true
    }
  }
}
```

</details>

<details>
<summary><b>Warp</b></summary>

Settings > MCP Servers > Add MCP Server:

```json
{
  "jobless": {
    "url": "https://mcp.jobless.dev/mcp",
    "headers": {
      "Authorization": "Bearer YOUR_API_KEY"
    }
  }
}
```

</details>

<details>
<summary><b>Kiro</b></summary>

Add to `~/.kiro/settings/mcp.json`:

```json
{
  "mcpServers": {
    "jobless": {
      "url": "https://mcp.jobless.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
```

</details>

<details>
<summary><b>Local install (pip, privacy mode)</b></summary>

Run the server locally as a subprocess. Your API key never touches `mcp.jobless.dev`:

```bash
pip install jobless-mcp
```

Then in your client config:

```json
{
  "mcpServers": {
    "jobless": {
      "command": "jobless-mcp",
      "env": {
        "JOBLESS_API_KEY": "YOUR_API_KEY"
      }
    }
  }
}
```

</details>

<details>
<summary><b>Other clients (generic remote MCP)</b></summary>

If your client supports remote MCP servers:

```json
{
  "mcpServers": {
    "jobless": {
      "url": "https://mcp.jobless.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
```

If your client only supports stdio, use [mcp-remote](https://github.com/anthropics/mcp-remote) as a bridge:

```json
{
  "mcpServers": {
    "jobless": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.jobless.dev/mcp", "--header", "Authorization: Bearer YOUR_API_KEY"]
    }
  }
}
```

</details>

## Tools

10 tools split into 6 reads and 4 grouped writes. Claude picks the right tool from the conversation.

### Reads

| Tool | Purpose | Tier |
|---|---|---|
| `get_best_matches` | Top personalized job matches ranked by BM25 against your parsed resume and archetypes. Quickstart 10/day, Free 50/day, Pro unlimited. | All |
| `get_job` | Full detail for a job by UUID: description, tech stack, seniority fit, location, salary, application URL. With `include_form=True` (default) also best-effort fetches the ATS application form schema under `application_form`; falls back to `null` if unavailable. | All |
| `search_jobs` | Keyword and filter search across the public job index. Browse mode when you want to explore beyond your matches. Filters: location, remote scope, job type, recency, country, city, sort. | All |
| `list_bookmarked_jobs` | Full bookmark map: status (saved/applied/in_process), rating, notes, employer response, timestamps. | All |
| `get_profile_status` | Tier, daily usage, has_resume, reset time. Useful at the start of a conversation or for quota checks. | All |
| `get_application_answers` | The user's saved Q&A bank (autofill source for repeated questions: work auth, salary expectations, etc.). | All |

### Writes

| Tool | Purpose | Tier |
|---|---|---|
| `manage_bookmark` | Add, remove, or update the status of a job bookmark. `action="add"` saves a job, `action="remove"` deletes it (destructive, irreversible), `action="update_status"` sets `status` (saved/applied/in_process) and/or `rating` (1-10). Replaces v0.2's `bookmark_job` and `track_application`. | All |
| `generate_document` | Generate a tailored resume or cover letter. `kind="resume"` rewrites bullets against the JD and persists a new Resume row. `kind="cover_letter"` produces an AI letter from a resume + job (deduplicated unless `force=true`). Returns `document_id` for chaining into `download_document`. Replaces v0.2's `tailor_resume` and `generate_cover_letter`. | Pro |
| `download_document` | Download a generated document. `kind="resume"` streams PDF; `kind="cover_letter"` supports `format="pdf"` (base64 inline or `save_path` to disk) and `format="text"` (UTF-8 string for email or text fields). Replaces v0.2's `download_resume` and `download_cover_letter`. | All |
| `apply_to_job` | Prepare or submit an application. `action="prepare"` runs the three-pass autofill (kit → profile → SavedAnswer) and returns the filled fields. `action="submit"` actually sends the application; it's IRREVERSIBLE and requires `confirm=True` (otherwise a dry-run preview is returned). For unsupported ATSes the response carries `browser_submission_required=true` so Claude can hand off to Playwright MCP or the Jobless extension. Replaces v0.2's `prepare_application` and `submit_application`. | Pro |

### Example conversation (end-to-end)

```
User: Find me 3 senior Python remote roles in Europe.
  -> get_best_matches with filters

User: Tell me more about #2.
  -> get_job(job_id)             # also returns application_form by default

User: Bookmark it and generate a tailored resume + cover letter.
  -> manage_bookmark(job_id, action="add")
  -> generate_document(kind="resume", job_id, resume_id)
  -> generate_document(kind="cover_letter", job_id, resume_id)

User: Prefill the form and submit.
  -> apply_to_job(job_id, action="prepare", resume_id, cover_letter_id)
  -> apply_to_job(job_id, action="submit", answers, confirm=True)

User: Mark it as applied.
  -> manage_bookmark(job_id, action="update_status", status="applied")
```

## Better with Playwright MCP

Some ATSes (Workday, BambooHR, custom careers pages) don't expose a clean POST endpoint, so `submit_application` returns:

```json
{
  "error": "browser_submission_required",
  "ats_platform": "WORKDAY",
  "apply_url": "https://acme.wd1.myworkdayjobs.com/...",
  "prepared_data": { ...autofill answers, resume URL, cover letter URL... }
}
```

Composed with [Microsoft's Playwright MCP](https://github.com/microsoft/playwright-mcp), Claude can finish the job in the browser:

```
jobless.apply_to_job(job_id, action="prepare", resume_id, cover_letter_id)
  -> { prepared_data, apply_url }

playwright.browser_navigate(apply_url)
playwright.browser_fill_form(prepared_data.answers)
playwright.browser_file_upload(prepared_data.resume_pdf_url)
playwright.browser_click("Submit")
playwright.browser_snapshot()         # confirmation screenshot

jobless.manage_bookmark(job_id, action="update_status", status="applied")
```

No other career MCP composes like this because no other career MCP has both the profile bank and the prepared-payload contract.

## Pricing

| | Quickstart | Free | Pro |
|---|---|---|---|
| **Daily matches** | 10 | 50 | Unlimited |
| **Signup required** | No | Yes | Yes |
| **Ranking** | Dual KNN | Dual KNN | ColBERT reranking |
| **Price** | Free | Free | $19/mo |

[Upgrade at jobless.dev/dashboard/premium](https://jobless.dev/dashboard/premium)

## Architecture

This package is a **thin wrapper** (~150 LOC) around the Jobless REST API. Two modes:

- **Stdio (local):** launched as a subprocess by your client. API key stays on your machine.
- **Streamable HTTP (hosted):** `mcp.jobless.dev`. No install needed. Bearer token in every request.

Both call the same closed-source Jobless backend (`api.jobless.dev`) which holds the OpenSearch index, Jina embeddings, and ranking models.

```
              github.com/bendza/jobless-mcp
            (MIT, ~150 LOC, this repo)
                      |
         +------------+------------+
         |                         |
   Local stdio mode         Hosted HTTP mode
   (pip install)            (mcp.jobless.dev)
         |                         |
         +------------+------------+
                      v
            api.jobless.dev (Django)
                      v
        OpenSearch + Postgres + Redis
```

## Self-Hosting

```bash
pip install jobless-mcp
JOBLESS_MCP_TRANSPORT=http \
JOBLESS_API_BASE=https://your-api.example.com \
PORT=8100 \
jobless-mcp
```

## Development

```bash
git clone https://github.com/bendza/jobless-mcp.git
cd jobless-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest -v
```

13 tests covering the client layer. All tests mock the API with [respx](https://github.com/lundberg/respx) and run offline in <100ms.

## Links

- **[jobless.dev/mcp](https://jobless.dev/mcp)** -- API key + install commands
- **[jobless.dev](https://jobless.dev)** -- the main product
- **[Issues](https://github.com/bendza/jobless-mcp/issues)** -- bug reports + feature requests

## License

MIT -- see [LICENSE](LICENSE).

[![Star History Chart](https://api.star-history.com/svg?repos=bendza/jobless-mcp&type=Date)](https://www.star-history.com/#bendza/jobless-mcp&Date)