Integration

CleanJobData MCP Server: Local Setup Guide

CleanJobData Engineering

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.

CleanJobData now has its own live MCP server

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. Takes title, 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, and count — the same filters as the underlying /jobs endpoint.
  • get_job — fetch one job by job_id, full detail included.
  • search_companies — find companies by query, website_url, employer_id, active, with limit/offset.
  • get_company — fetch one company by company_id.
  • suggest_locations — location autocomplete, taking query, kinds, and limit, 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 | sh

The 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-mcp

To 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-mcp

There'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:latest

Configuration

The server reads its config from environment variables:

VariableRequiredPurpose
CLEANJOBDATA_API_KEYYesYour CleanJobData API key
CLEANJOBDATA_API_BASENoOverride the API base URL (defaults to https://api.cleanjobdata.com)
MCP_TRANSPORTNostdio (default) or http
HOST / PORTNoOnly relevant in http mode — default 127.0.0.1:8000
CLEANJOBDATA_ALLOW_ENV_KEY_FALLBACKNoSet 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-mcp

Run 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 8000

This 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_jobs with remote=true, experience_level set to senior, and salary_min=150000.
  • "What's the full description for job 4837291?" → calls get_job with job_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_companies with query=acme.
  • "Tell me more about company 512" → calls get_company with company_id=512, pulling the enrichment data (logo, headcount, etc.).
  • "What's the location ID for Austin, Texas?" → calls suggest_locations with query=Austin, Texas — mostly useful behind the scenes when you ask a location-filtered question and Claude needs a real city_id to pass into search_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_profile prompt, which structures your resume text, LinkedIn URL, and personal site into a profile Claude then uses to guide a search_jobs conversation.

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.