open-greenhouse-mcp
This MCP server provides a comprehensive set of tools for interacting with Greenhouse ATS, supporting candidate management, pipeline workflows, screening, analytics, and more, with role-based access control.
Candidate Management: Search by name or exact email; retrieve full profiles, activity feeds, and resumes; add notes and tags; download attachments.
Application & Pipeline Management: List, get, advance, reject, and unreject applications individually or in bulk; view pipeline summaries; identify stale applications and candidates needing action.
Job & Stage Information: List and get job details; retrieve pipeline stages.
Screening & Sourcing: Full candidate screening packages; daily new application digests; boolean keyword searches on resumes; structured field searches (title, company, education, tags); batch resume reading.
Interviews & Scorecards: List and retrieve scorecards for applications.
Reporting & Analytics: Pipeline conversion metrics, source effectiveness, time-to-hire calculations.
Bulk Operations: Efficiently tag, reject, or advance multiple candidates/applications with rate-limit handling.
Reference Data: List rejection reasons.
Access Control: Operates in read-only, recruiter (default), or full profiles for write safety.
Observability: Structured JSON logging and diagnostics for troubleshooting.
Allows interaction with the Greenhouse recruiting platform, providing tools for candidate screening, pipeline management, analytics, and recruiter workflows such as bulk reject, advance, and tagging.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@open-greenhouse-mcp Who needs my attention this week?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
open-greenhouse-mcp
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 |
| 103 | No | First-time setup, reporting, hiring managers |
| 127 | Yes (safe ops) | Day-to-day recruiting work |
| 181 | Yes (all) | Admins, ops, advanced automation |
Related MCP server: Greenhouse MCP Server by CData
Quick Start
pip install open-greenhouse-mcpAdd 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_KEYno 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

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_OFBulk actions are rate-limited to stay within API limits
Compatibility
Client | Status |
Supported | |
Supported | |
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, ingestionWhat'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 |
| Complete screening package — profile, resume text, location, screening answers, job description, history |
| Applications since a date, grouped by job — the daily recruiter digest |
| Search resume text across pipelines with boolean keywords (required/preferred/exclude) |
| Search pipelines by structured fields — title, company, education, experience, tags |
| Database-wide candidate search by structured fields with date bounds |
| 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 |
| Full pipeline view — candidates grouped by stage with names and days-in-stage |
| Find stale applications and interviews missing scorecards |
| Applications with no activity for N days, sorted by stalest |
| Conversion rates, hire/rejection rates, time-in-stage per stage |
| Which candidate sources produce the best hire rates |
| Average, median, min, max days from application to hire |
| Reject multiple applications in one call with rate-limit handling |
| Tag multiple candidates in one call |
| Advance multiple applications to next stage |
| Find candidates by first or last name |
| Look up a candidate by exact email |
| Download and return a candidate's most recent resume |
| 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 |
| Yes* | Harvest v3 client ID |
| Yes* | Harvest v3 client secret |
| No | Your Greenhouse user ID — attributes v3 calls and derives the tool profile |
| Yes* | Job board URL slug. *Client credentials or a board token required |
| No | Legacy v1 key — Job Board / Ingestion APIs only, not Harvest |
| No | Set |
| No | Set |
| No |
|
| No | Greenhouse user ID for write audit trail |
| No |
|
| No | Log file path (defaults to stderr) |
| No | Tool-result size budget in bytes (default |
| No | Diagnostics file path (defaults beside Claude's logs) |
| No | Set |
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_BYTESto override). A 500-job/jobspage 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 — attachingreturned/total_foundand a note telling the model to narrow by a real filter or walk pages, rather than telling the user about flags. Composite tools callinglist_*internally still get complete data.User-relayable errors — every failure carries a plain-English
user_message, asupport_codelikeGH403-0730-1421-7F2Dthe user can paste into a support request, anduser_can_resolveto 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=offto disable.Default profile is now
recruiter, notfull— an unset or unrecognisedGREENHOUSE_TOOL_PROFILEused to register every tool with writes enabled, including destructive ones. Explicit values, includingfull, 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
assistanttool profile, and a safe default profile instead offullA 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 allA 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/mainFeedback
Bugs and features: Open an issue
Questions: Start a discussion
Security: See SECURITY.md
Contributing: See CONTRIBUTING.md
License
MIT License -- Ben Monopoli. See LICENSE.
Available Tools
33 toolsadd_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
Scored across 33 tools
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.
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.
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.
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
Related MCP Connectors
Public MCP server for discovering open jobs. Search, filter, and get application links.
Hosted MCP server for the Healthie EHR & telehealth API: patients, appointments, charting, tasks.
Siftable MCP server for governed work, knowledge, calendar, people, datasets, and Vault.
isolved and ApplicantPro jobs, tenant discovery, and change detection as an MCP server.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA 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-
- AlicenseNot gradedqualityDmaintenanceThis 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=betaMIT
- AlicenseAqualityBmaintenanceProduction-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.18786 PyPI5MIT
- AlicenseNot gradedqualityDmaintenanceEnables querying and managing Greenhouse recruiting data through MCP, including applications, candidates, jobs, and rejection operations.MIT