CleanJobData MCP Server: Local Setup Guide
TL;DR
cleanjobdata-mcp is a third-party, MIT-licensed MCP server built by Jake Gaylor (jhgaylor) that wraps the CleanJobData API as five tools (search_jobs, get_job, search_companies, get_company, suggest_locations) for any MCP-compatible AI client. Install with uvx cleanjobdata-mcp, set CLEANJOBDATA_API_KEY, and point your client's MCP config at it — stdio by default, with an HTTP transport mode available for multi-user or remote setups.
You can use it by following the instructions at Connect via MCP — no local setup needed. Keep reading below if you'd rather run the server yourself instead.
A quick note before the how-to: this MCP server isn't something CleanJobData built or maintains. It's a community project — cleanjobdata-mcp, created by Jake Gaylor, MIT-licensed, published on PyPI. It's a genuinely useful piece of tooling, and it's worth knowing how to set up if you want an AI assistant like Claude to query CleanJobData directly during a conversation — but the credit for building it belongs to its author, not us.
What It Actually Does
MCP (Model Context Protocol) is the standard that lets tools like Claude Desktop, Claude Code, and Cursor call external APIs as part of a conversation. cleanjobdata-mcp wraps the CleanJobData API as five tools an MCP-compatible client can call:
search_jobs— the main one. Takestitle,sort_by,city_id,state_id,country_id,location,remote,remote_type,company_name,employer_id,salary_min,salary_max,require_salary,experience_level,employment_type,published_after,max_age,include_expired,include_description,limit,cursor, andcount— the same filters as the underlying/jobsendpoint.get_job— fetch one job byjob_id, full detail included.search_companies— find companies byquery,website_url,employer_id,active, withlimit/offset.get_company— fetch one company bycompany_id.suggest_locations— location autocomplete, takingquery,kinds, andlimit, resolving free-text into the city/state/country IDs the other tools expect.
There's also a prompt helper, create_candidate_profile, that takes name, linkedin_url, personal_website, and resume_text and generates a structured prompt to guide a job search conversation from a candidate's profile.
Prerequisites
- A CleanJobData API key — get one free at cleanjobdata.com.
uv, the Python package manager the project uses. Install it with:
curl -LsSf https://astral.sh/uv/install.sh | shThe server itself is Python-based, published as cleanjobdata-mcp on PyPI — you don't need to already have Python project tooling set up beyond uv.
Installing and Running It
The simplest path is uvx, which runs the published package without a separate install step:
uvx cleanjobdata-mcpTo run from source instead (useful if you want to inspect or modify it):
git clone https://github.com/jhgaylor/cleanjobdata-mcp
uv run --directory /path/to/cleanjobdata-mcp cleanjobdata-mcpThere's also a published Docker image, if you'd rather run it as a container:
docker run --rm -p 8000:8000 ghcr.io/jhgaylor/cleanjobdata-mcp:latestConfiguration
The server reads its config from environment variables:
| Variable | Required | Purpose |
|---|---|---|
CLEANJOBDATA_API_KEY | Yes | Your CleanJobData API key |
CLEANJOBDATA_API_BASE | No | Override the API base URL (defaults to https://api.cleanjobdata.com) |
MCP_TRANSPORT | No | stdio (default) or http |
HOST / PORT | No | Only relevant in http mode — default 127.0.0.1:8000 |
CLEANJOBDATA_ALLOW_ENV_KEY_FALLBACK | No | Set true to allow HTTP-mode clients to fall back to the server's own env-configured key instead of supplying their own (off by default) |
For a local single-user setup — the common case — you only need CLEANJOBDATA_API_KEY. The default stdio transport is what Claude Desktop and Claude Code expect; you don't need to touch MCP_TRANSPORT unless you're deliberately running it as a shared HTTP service (see below).
Wiring It Into Claude Desktop
Add this to your Claude Desktop MCP config:
{
"mcpServers": {
"cleanjobdata": {
"command": "uvx",
"args": ["cleanjobdata-mcp"],
"env": {
"CLEANJOBDATA_API_KEY": "your-api-key-here"
}
}
}
}Restart Claude Desktop after saving, and the tools above become available in any conversation.
Wiring It Into Claude Code
Claude Code manages MCP servers through its own CLI rather than a hand-edited config file. Add the server with:
claude mcp add cleanjobdata --env CLEANJOBDATA_API_KEY=your-api-key-here -- uvx cleanjobdata-mcpRun claude mcp list afterward to confirm it's registered, and it'll be available in your next session.
Wiring It Into Cursor
In Cursor: Settings → MCP → Add Server, set the command to uvx cleanjobdata-mcp, and add CLEANJOBDATA_API_KEY as an environment variable in the same dialog.
Running It as a Shared HTTP Server
If you want one running instance shared across a team, or accessible remotely rather than launched per-client, run it in HTTP mode instead of the default stdio:
cleanjobdata-mcp --transport http --host 0.0.0.0 --port 8000This exposes POST /mcp (the streamable HTTP MCP endpoint) and an unauthenticated GET /healthz liveness probe for monitoring. Clients connecting over HTTP authenticate per-request with either an Authorization: Bearer <cleanjobdata-api-key> or X-CleanJobData-API-Key: <cleanjobdata-api-key> header — so in this mode, individual users supply their own key on each request rather than the server holding one shared key (unless you've explicitly enabled CLEANJOBDATA_ALLOW_ENV_KEY_FALLBACK).
A remote-mode Claude Desktop config looks like this instead of the stdio version above:
{
"mcpServers": {
"cleanjobdata": {
"url": "https://your-host.example.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_CLEANJOBDATA_API_KEY"
}
}
}
}How to Use It
Once the server's wired in and your client has restarted, you don't call the tools directly — you just talk to Claude (or Cursor) in plain language, and it decides which tool to call and with which parameters. A few concrete examples, mapped to what's actually happening behind each one:
- "Find me remote senior backend roles paying over $150k" → calls
search_jobswithremote=true,experience_levelset to senior, andsalary_min=150000. - "What's the full description for job 4837291?" → calls
get_jobwithjob_id=4837291— useful once a search result gives you an ID and you want the detail the list view left out. - "What companies match 'acme' in their name or domain?" → calls
search_companieswithquery=acme. - "Tell me more about company 512" → calls
get_companywithcompany_id=512, pulling the enrichment data (logo, headcount, etc.). - "What's the location ID for Austin, Texas?" → calls
suggest_locationswithquery=Austin, Texas— mostly useful behind the scenes when you ask a location-filtered question and Claude needs a realcity_idto pass intosearch_jobs, rather than something you'd typically ask for directly. - "Here's my resume and LinkedIn — help me find matching roles" → triggers the
create_candidate_profileprompt, which structures your resume text, LinkedIn URL, and personal site into a profile Claude then uses to guide asearch_jobsconversation.
You don't need to know the exact tool names or parameters to use any of this — that's the point of MCP. The list above is really for understanding what's happening when you ask a question, useful mainly if a result looks wrong and you want to reason about which tool and filters actually got called.
Where This Fits
This is a client-side convenience layer, not a replacement for the API itself — it's calling the same /jobs, /companies, and /geo/suggest endpoints you'd hit directly, just exposed as tools an AI assistant can call mid-conversation instead of you writing requests by hand. If you're building a product rather than exploring data conversationally, you'll still integrate the REST API directly, the same way covered throughout the rest of these guides.