Skip to main content
Glama
open-greenhouse-mcp

open-greenhouse-mcp

by Comradery64

open-greenhouse-mcp

PyPI CI Python 3.10+ License: MIT open-greenhouse-mcp MCP server

Production-ready MCP server for Greenhouse, designed for recruiters and hiring teams.

Most Greenhouse MCP servers mirror the API endpoint by endpoint. This one is built for recruiting teams: safe defaults, role-based profiles, and workflow tools that turn multi-step API operations into single actions.

Choose a Profile

Profile

Tools

Can write?

Recommended for

read-only

103

No

First-time setup, reporting, hiring managers

recruiter (default)

127

Yes (safe ops)

Day-to-day recruiting work

full

181

Yes (all)

Admins, ops, advanced automation

Related MCP server: Greenhouse MCP Server by CData

Quick Start

pip install open-greenhouse-mcp

Add to your MCP client config (Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json, Cursor: Settings > MCP):

{
  "mcpServers": {
    "greenhouse": {
      "command": "open-greenhouse-mcp",
      "env": {
        "GREENHOUSE_CLIENT_ID": "your-harvest-client-id",
        "GREENHOUSE_CLIENT_SECRET": "your-harvest-client-secret",
        "GREENHOUSE_TOOL_PROFILE": "read-only"
      }
    }
  }
}

Start in read-only mode to validate connectivity and tool behaviour, then switch to recruiter or full when you need write access.

Your client ID and secret come from Greenhouse under Configure > Dev Center > API Credential Management, created by someone with Can manage ALL organization's API Credentials. Harvest v3 grants access per endpoint, so the credential needs scopes for the endpoints you intend to use.

Harvest v3. Harvest v1 and v2 became unavailable after 2026-08-31, so the older per-person GREENHOUSE_API_KEY no longer opens Harvest. Tools not yet verified against the v3 guides are withheld at startup rather than left registered and broken — see docs/harvest-v3-migration.md.

What You Can Ask

  • "Show me the pipeline for our Senior Engineer role"

  • "Who needs my attention this week?"

  • "What are our conversion rates for the Backend Intern role?"

  • "Find Sarah Chen and pull up her resume"

  • "Which sources are actually producing hires?"

  • "Bulk reject everything inactive for 30+ days on the Account Manager role"

  • "Screen this candidate for the Backend Engineer role — give me the full picture"

  • "Search our engineering pipelines for anyone with Rust and distributed systems experience"

  • "What new applications came in since yesterday?"

See more examples with full output.

See it in action

Demo

Safety

  • Access is limited by your Greenhouse API key permissions

  • Read-only profile is recommended for first setup

  • Destructive actions require explicit IDs — the server never infers targets

  • Write operations support audit attribution via GREENHOUSE_ON_BEHALF_OF

  • Bulk actions are rate-limited to stay within API limits

Compatibility

Client

Status

Claude Desktop

Supported

Claude Code

Supported

Cursor

Supported

Transport

stdio

Python

3.10+

Startup

When the server starts, it logs its configuration:

open-greenhouse-mcp v0.4.0
Profile: recruiter | Tools: 127 | Writes: recruiter-safe | APIs: harvest, ingestion

What's Included

  • Screening & sourcing tools — 6 tools for candidate screening, resume search with boolean keywords, daily digest, and location detection

  • Recruiter workflow tools — 13 composite tools for pipeline views, analytics, search, and bulk operations

  • Harvest API coverage — 148 tools across candidates, applications, jobs, offers, interviews, and more

  • Job Board API — 13 tools for public job listings and application submission

  • Optional webhooks and ingestion — 14 tools for event-driven workflows and partner integrations


Reference

Screening & Sourcing Tools

Tools for candidate evaluation and proactive talent search.

Tool

What it does

screen_candidate

Complete screening package — profile, resume text, location, screening answers, job description, history

fetch_new_applications

Applications since a date, grouped by job — the daily recruiter digest

scan_pipeline_resumes

Search resume text across pipelines with boolean keywords (required/preferred/exclude)

search_pipeline_candidates

Search pipelines by structured fields — title, company, education, experience, tags

scan_all_candidates

Database-wide candidate search by structured fields with date bounds

batch_read_resumes

Batch-fetch and extract resume text for multiple candidates

Composite Tools

High-level tools that combine multiple API calls into single operations.

Tool

What it does

pipeline_summary

Full pipeline view — candidates grouped by stage with names and days-in-stage

candidates_needing_action

Find stale applications and interviews missing scorecards

stale_applications

Applications with no activity for N days, sorted by stalest

pipeline_metrics

Conversion rates, hire/rejection rates, time-in-stage per stage

source_effectiveness

Which candidate sources produce the best hire rates

time_to_hire

Average, median, min, max days from application to hire

bulk_reject

Reject multiple applications in one call with rate-limit handling

bulk_tag

Tag multiple candidates in one call

bulk_advance

Advance multiple applications to next stage

search_candidates_by_name

Find candidates by first or last name

search_candidates_by_email

Look up a candidate by exact email

read_candidate_resume

Download and return a candidate's most recent resume

download_attachment

Download any Greenhouse attachment by URL

Profile Details

Recruiter includes all read tools, all screening/sourcing tools, all composite workflows, and recruiter-safe writes: reject, advance, hire, move, tag, notes, attachments, interviews, prospects, and bulk operations. It excludes job creation, user management, custom field configuration, candidate deletion, and webhook management.

Read-only skips all write operations. GREENHOUSE_READ_ONLY=true also works as a shorthand.

Configuration

Variable

Required

Description

GREENHOUSE_CLIENT_ID

Yes*

Harvest v3 client ID

GREENHOUSE_CLIENT_SECRET

Yes*

Harvest v3 client secret

GREENHOUSE_USER_ID

No

Your Greenhouse user ID — attributes v3 calls and derives the tool profile

GREENHOUSE_BOARD_TOKEN

Yes*

Job board URL slug. *Client credentials or a board token required

GREENHOUSE_API_KEY

No

Legacy v1 key — Job Board / Ingestion APIs only, not Harvest

GREENHOUSE_ALLOW_UNMIGRATED_TOOLS

No

Set 1 to register Harvest tools not yet verified against v3

GREENHOUSE_STRICT_PROJECTION

No

Set 1 to fail (not warn) when a payload field is missing

GREENHOUSE_TOOL_PROFILE

No

recruiter (default), read-only, or full

GREENHOUSE_ON_BEHALF_OF

No

Greenhouse user ID for write audit trail

GREENHOUSE_LOG_LEVEL

No

debug, info, warning (default), error

GREENHOUSE_LOG_FILE

No

Log file path (defaults to stderr)

GREENHOUSE_MAX_RESULT_BYTES

No

Tool-result size budget in bytes (default 60000)

GREENHOUSE_DIAGNOSTICS_FILE

No

Diagnostics file path (defaults beside Claude's logs)

GREENHOUSE_DIAGNOSTICS

No

Set off to disable the diagnostics file

Logging

Structured JSON logging for observability. Set GREENHOUSE_LOG_LEVEL=info to enable:

{"ts": "2026-04-14T12:31:58", "level": "info", "event": "api_call", "method": "GET", "url": "...", "status": 200, "latency_ms": 245.0}

More Documentation

  • API Reference — Full tool breakdown by category

  • Usage Examples — Real conversations with full output

  • Advanced Setup — Webhook receiver, ingestion API, board-token mode

  • Development — Contributing, testing, project structure

  • Handoff — start here: current state, the verified v3 contract, and the traps that cost real records to find

  • Harvest v3 migration — full endpoint detail. Reads are verified live; writes are not, and 121 tools stay withheld until they are

Changelog

Current version: 0.5.5. Full detail for every release lives in CHANGELOG.md; this is the short version.

0.5.5 — reliability and error reporting

Aimed at deployments where the people using the tools are recruiters, not engineers, so a failure has to be self-explanatory and reportable.

  • Result-size shaping — results are measured and kept within a size budget (60KB default, GREENHOUSE_MAX_RESULT_BYTES to override). A 500-job /jobs page runs to ~1.1MB and clients reject an oversized tool result outright, so the user saw a bare failure instead of an answer. Shaping degrades lazily: untouched if it already fits, then field projection, then text clamping, then dropping rows — attaching returned/total_found and a note telling the model to narrow by a real filter or walk pages, rather than telling the user about flags. Composite tools calling list_* internally still get complete data.

  • User-relayable errors — every failure carries a plain-English user_message, a support_code like GH403-0730-1421-7F2D the user can paste into a support request, and user_can_resolve to separate "check the spelling" from "escalate, you cannot fix this".

  • Always-on diagnostics file — notable events append to a JSON-lines file at a fixed path, so support is "send me this file" instead of asking a recruiter to reproduce with logging turned up. GREENHOUSE_DIAGNOSTICS=off to disable.

  • Default profile is now recruiter, not full — an unset or unrecognised GREENHOUSE_TOOL_PROFILE used to register every tool with writes enabled, including destructive ones. Explicit values, including full, are unchanged.

  • Fixed: 400 and 409 responses were treated as success data — only an enumerated set of statuses became errors, so a rejected filter value came back looking like a real record. Any status >= 400 is now an error.

0.4.0 — screening and sourcing

screen_candidate, fetch_new_applications, search_pipeline_candidates, scan_all_candidates, batch_read_resumes, and scan_pipeline_resumes, plus server-side PDF/DOCX resume text extraction and a 5-step location detection cascade.

0.3.0 — profiles and logging

Tool profiles via GREENHOUSE_TOOL_PROFILE (full / recruiter / read-only) and structured JSON logging with per-call method, status, and latency.

0.2.1 — packaging

PyPI metadata: keywords, classifiers, and project URLs.

0.2.0 — composite tools

13 composite tools for recruiter workflows.

0.1.0 — initial release

Harvest, Job Board, and Ingestion API coverage.

Skills

skills/ holds the Claude skills that drive these tools for recruiters — application triage, candidate screening, pipeline search, and resume batch review. Skills are distributed separately from the extension bundle: upload each directory to Claude as a skill, alongside installing the .mcpb.

They name MCP tools in prose, so a rename or a tool dropped from the bundle's pinned profile would break them silently, at the moment a recruiter tried to use one. tests/test_skills.py matches the registered tool set against each skill's text and fails the build instead. Cited tools are discovered from the files rather than a hardcoded list, so newly cited tools are covered automatically.

Relationship to upstream

This project began as a fork of benmonopoli/open-greenhouse-mcp (MIT, Copyright © 2026 Ben Monopoli), which remains the origin of the great majority of this code. The LICENSE file is unchanged and continues to carry that notice.

Changes made here, released as 0.5.5:

  • Result-size shaping, so a large tool result is trimmed to fit rather than rejected

  • User-relayable error messages carrying a support code a non-technical user can pass on

  • An always-on diagnostics file, so support does not depend on reproducing a failure

  • A curated assistant tool profile, and a safe default profile instead of full

  • A fix for error statuses being treated as success data

  • An upper bound on mcp, without which a clean install resolves 2.0.0 and the package cannot be imported at all

  • A release workflow producing a cross-platform Claude Desktop bundle

To pull in future upstream work:

git remote add upstream https://github.com/benmonopoli/open-greenhouse-mcp.git
git fetch upstream && git merge upstream/main

Feedback

License

MIT License -- Ben Monopoli. See LICENSE.

Available Tools

33 tools
add_note_to_candidateA

Add a text note to a candidate's activity feed. Write operation.

Users say "add a note to Sarah's profile" or "log that I spoke with John." To get candidate_id: search_candidates_by_name.

Inputs: body (required), candidate_id (required), visibility. Returns: result

add_tag_to_candidateA

Apply a tag to a candidate. Write operation.

Users say "tag Sarah as 'referred'" or "mark John as strong hire." For candidate_id: search_candidates_by_name. For tag_id: list_tags → match by name. For bulk tagging, use bulk_tag instead.

Inputs: tag_id (required), candidate_id (required). Returns: result

advance_applicationA

Move a candidate forward one stage in their job pipeline. Write operation.

Users say "advance Sarah to the next stage" or "move John forward." To get the application_id: search_candidates_by_name → get_candidate → match the application to the job. Stage IDs are optional — omit to advance to the natural next stage. To skip stages, use move_application_same_job. For bulk advancing, use bulk_advance.

Inputs: from_stage_id (required), application_id (required), to_stage_id. Returns: result

bulk_advanceA

Advance multiple applications to the next stage. Write operation — rate-limited.

Users say "move everyone past phone screen forward." Get application_ids from pipeline_summary or list_applications. Optionally specify from_stage_id (list_job_stages_for_job → match by name) to only advance candidates in that specific stage. Processes sequentially with rate-limit delays.

Inputs: application_ids (required), from_stage_id. Returns: result

bulk_rejectA

Reject multiple applications in one call. Write operation — rate-limited.

Users say "reject everyone who's been inactive for 30 days on the Backend role." First use stale_applications to identify the targets, then pass their application_ids here. For rejection_reason_id: list_rejection_reasons → match by name. Processes sequentially with rate-limit delays.

Inputs: application_ids (required), rejection_email, rejection_reason_id. Returns: result

bulk_tagA

Tag multiple candidates in one call. Write operation — rate-limited.

Users say "tag all the candidates from the hiring event." Pass candidate_ids from search or pipeline tools and a tag_name (created automatically if new). Processes sequentially with rate-limit delays.

Inputs: tag_name (required), candidate_ids (required). Returns: result

candidates_needing_actionA

Find candidates that need attention — stale apps, missing scorecards. Read-only.

Users say "what needs my attention?" or "who's been sitting too long?" Pass job_id for one job (list_jobs → match by name) or omit for all active applications. Returns stale applications sorted by urgency and interviews missing scorecards.

Inputs: job_id, stale_days. Returns: result

download_attachmentA

Download content from a Greenhouse attachment URL. Read-only.

Use when you have a specific attachment URL from a candidate or application record (e.g., from get_candidate's attachments array).

Inputs: url (required). Returns: result

fetch_new_applicationsA

Applications since a date, grouped by job — the daily digest. Read-only.

Users say "what new applications came in since yesterday?" Pass since as an ISO date. Optionally filter to one job with job_id (list_jobs → match by name). Returns applications grouped by job with candidate names, sources, stages, and screening answers.

Inputs: since (required), job_id, status, include_candidate_details. Returns: result

get_activity_feedA

Get a candidate's activity timeline. Read-only.

Users say "show me Sarah's history" or "what's happened with this candidate?" To find candidate_id: search_candidates_by_name. Returns notes, emails, stage changes, and other timeline events in chronological order.

Inputs: candidate_id (required). Returns: result

get_applicationA

Get a single application by ID. Read-only.

Users rarely know application IDs. To find one: search_candidates_by_name → get_candidate → the applications array has each application's ID and job name. For a complete screening package with resume and location, use screen_candidate.

Inputs: application_id (required). Returns: result

get_candidateA

Get a candidate's full profile by ID. Read-only.

Returns name, contact info, all applications (with job names and statuses), tags, custom fields, and attachments. This is the central lookup — most workflows route through here after resolving a name via search_candidates_by_name. For a screening package with resume and location, use screen_candidate.

Inputs: candidate_id (required). Returns: result

get_jobA

Get full details for a job by ID. Read-only.

Returns name, status, departments, offices, hiring team, and custom fields. Use list_jobs to find the job_id by name first. For the public listing, use list_job_posts_for_job. For pipeline stages, use list_job_stages_for_job.

Inputs: job_id (required). Returns: result

get_scorecardA

Get a single scorecard by ID. Read-only.

Returns the interviewer's ratings, attribute scores, and overall recommendation. To find scorecard IDs: list_scorecards_for_application.

Inputs: scorecard_id (required). Returns: result

list_applicationsA

List applications with optional filters. Read-only.

Users say "show me applications for [job name]" or "what came in this week." To filter by job: list_jobs → find by name → use its job_id. To filter by candidate: search_candidates_by_name → candidate_id. For pipeline views grouped by stage, use pipeline_summary. For stale candidates, use stale_applications or candidates_needing_action.

Inputs: cursor, job_id, status, paginate, +5 more. Returns: result

list_candidatesA

List candidates with optional filters. Read-only.

For finding a specific person, use search_candidates_by_name (by name) or search_candidates_by_email (by email) — faster and simpler. Use this tool for bulk operations: date-range queries, fetching by specific IDs, or paginating through the full database.

Inputs: email, cursor, paginate, per_page, +5 more. Returns: result

list_jobsA

List all jobs with optional filters. Read-only.

This is the primary tool for resolving job titles to job IDs. When a user mentions a job by name ("Backend Engineer"), use this to find the matching job_id. Filter by status ('open'/'closed'/'draft'), department_id (list_departments), or office_id (list_offices). For pipeline views, use pipeline_summary with the job_id.

Inputs: cursor, status, paginate, per_page, +4 more. Returns: result

list_job_stages_for_jobA

List pipeline stages for a specific job in order. Read-only.

This is the primary tool for resolving stage names to stage IDs. When a user says "move to the onsite stage," use this to find the stage_id. To find the job_id first: list_jobs → match by name.

Inputs: job_id (required). Returns: result

list_rejection_reasonsA

List all rejection reasons. Read-only.

Resolves rejection reason names to IDs. When a user says "reject for 'not enough experience'," use this to find the ID, then pass it to reject_application or bulk_reject.

Inputs: cursor, per_page, force_refresh. Returns: result

list_scorecards_for_applicationA

List scorecards submitted for a specific application. Read-only.

To find application_id: search_candidates_by_name → get_candidate → match the application to the job. Returns each interviewer's ratings and overall recommendation.

Inputs: application_id (required). Returns: result

pipeline_metricsA

Conversion rates and stage metrics for a job. Read-only.

Users say "what are our conversion rates for the Backend role?" or "where are we losing candidates?" To find job_id: list_jobs → match by name. Returns per-stage counts, conversion percentages, and time-in-stage metrics.

Inputs: job_id (required). Returns: result

pipeline_summaryA

Complete pipeline view for a job — candidates grouped by stage. Read-only.

Users say "show me the pipeline for Backend Engineer" or "how many candidates are in each stage." To find the job_id: list_jobs → match by name. Returns stages with candidate counts, names, days-in-stage, and last activity. One call replaces 5-10 sequential API calls.

Inputs: job_id (required). Returns: result

read_candidate_resumeA

Download and return a candidate's most recent resume text. Read-only.

Users say "pull up Sarah's resume" or "show me John's CV." To find candidate_id: search_candidates_by_name. Returns extracted text from the most recent resume attachment. For batch reading, use batch_read_resumes.

Inputs: candidate_id (required). Returns: result

reject_applicationA

Reject a candidate from a job. Write operation.

Users say "reject Sarah from the Backend role." To get the application_id: search_candidates_by_name → get_candidate → match the application to the job. For rejection_reason_id: list_rejection_reasons → match by name. For email templates: list_email_templates. Can be reversed with unreject_application. For bulk rejections, use bulk_reject.

Inputs: application_id (required), notes, rejection_email, rejection_reason_id. Returns: result

scan_pipeline_resumesA

Search resume text in pipelines for skills and qualifications. Read-only.

Users say "find Rust engineers in our pipelines" or "search for distributed systems experience." The primary sourcing tool — ~90% of candidate data lives in resumes. Pass job_ids (list_jobs → get IDs for similar roles). Supports boolean search: keywords (OR), required_keywords (AND), exclude_keywords (NOT with word-boundary matching).

Inputs: job_ids (required), keywords, statuses, max_resumes, +2 more. Returns: result

screen_candidateA

Full screening package for one candidate application. Read-only.

Users say "screen Sarah for the Backend role" or "give me the full picture." To get application_id: search_candidates_by_name → get_candidate → match the application to the job name. Returns profile, resume text, location, screening answers, job description, and application history in one call.

Inputs: application_id (required). Returns: result

search_candidates_by_emailA

Look up a candidate by exact email address. Read-only.

Use when the user provides an email instead of a name. Returns the candidate record directly. For name-based lookup, use search_candidates_by_name.

Inputs: email (required). Returns: result

search_candidates_by_nameA

Find candidates by name. Read-only — the starting point for most workflows.

Users always refer to candidates by name, not ID. Use this first whenever a user mentions a candidate, then get_candidate on the match to see their full profile and application IDs. Case-insensitive substring match — "Sarah" finds "Sarah Chen", "Sarah O'Brien", etc.

Inputs: name (required), per_page, max_pages. Returns: result

search_pipeline_candidatesA

Search pipelines by structured fields — title, company, education, tags. Read-only.

Users say "find VP-level candidates in our pipelines" or "who do we have from Google?" Pass job_ids (list_jobs → get IDs for similar roles). Best when structured data is populated. If few results, switch to scan_pipeline_resumes for resume-text search. Combine with batch_read_resumes to verify skill matches.

Inputs: job_ids (required), tags, statuses, max_results, +4 more. Returns: result

source_effectivenessA

Which candidate sources produce the best results. Read-only.

Users say "which sources are working?" or "where should we spend recruiting budget?" Pass job_id (list_jobs → match by name) for one role, or omit for org-wide analysis. Returns volume, active rate, and hire rate per source.

Inputs: job_id, created_after. Returns: result

stale_applicationsA

Applications with no activity for N days, sorted by stalest. Read-only.

Users say "who's been sitting untouched?" Use the results with bulk_reject for pipeline cleanup. Pass job_id (list_jobs → match by name) to filter to one job.

Inputs: days, limit, job_id. Returns: result

time_to_hireA

Time-to-hire metrics for hired candidates. Read-only.

Users say "how long does it take to hire?" or "what's our average days-to-offer?" Pass job_id (list_jobs → match by name) for one role, or omit for org-wide metrics. Returns average, median, min, max days.

Inputs: job_id, created_after. Returns: result

unreject_applicationA

Reverse a rejection, returning the candidate to active status. Write operation.

Users say "undo the rejection for Sarah" or "bring Sarah back." To get the application_id: search_candidates_by_name → get_candidate → find the rejected application in their applications array.

Inputs: application_id (required). Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 33 tools

    Disambiguation4/5

    Most tools target distinct resources and actions, and descriptions include routing hints that clarify when to use each one. Some overlap exists among application-listing tools (list_applications, fetch_new_applications, stale_applications, pipeline_summary) and resume-search tools, but the intended boundaries are mostly clear.

    Naming Consistency4/5

    The majority of tools follow a snake_case verb_noun pattern like list_jobs, get_candidate, and reject_application. A few analytic tools break the pattern (pipeline_summary, source_effectiveness, time_to_hire, candidates_needing_action), and there is some verb variation across list/fetch/search/read, but the overall convention is predictable.

    Tool Count2/5

    33 tools is a heavy surface for an MCP server, exceeding the 25+ threshold where the set becomes difficult for an agent to navigate. While the ATS domain is broad, several tools overlap in capability and could be consolidated or omitted.

    Completeness2/5

    There are significant gaps, including referenced tools that do not exist in the set: list_tags, list_email_templates, and move_application_same_job are mentioned in descriptions but unavailable. There are also no create/update operations for candidates or jobs, and add_tag_to_candidate depends on a missing tag-listing tool, which will cause agent failures.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      A server implementation that enables interaction with Greenhouse's recruitment and applicant tracking system through Model Context Protocol, providing tools for job listings, candidate management, application filtering, and stage transitions.
      1
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      This project builds a read-only MCP server. For full read, write, update, delete, and action capabilities and a simplified setup, check out our free CData MCP Server for Greenhouse (beta): https://www.cdata.com/download/download.aspx?sku=PGZK-V&type=beta
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Production-ready MCP server for Greenhouse ATS with 175 tools for recruiting teams — manage candidates, applications, jobs, interviews, and hiring pipelines. Role-based profiles (full/recruiter/read-only), composite workflow tools for pipeline views, analytics, candidate search, and bulk operations.
      187
      86 PyPI
      5
      MIT