Jobless MCP
by bendza
README.md
[](https://cursor.com/en/install-mcp?name=jobless&config=eyJ1cmwiOiAiaHR0cHM6Ly9tY3Auam9ibGVzcy5kZXYvbWNwIn0%3D)
[](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)
[](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).
[](https://www.star-history.com/#bendza/jobless-mcp&Date)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing