openhire
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Fail
- new Function() — Dynamic code execution via Function constructor in design_handoff_openhire_v01/design_refs/support.js
- exec() — Shell command execution in design_handoff_openhire_v01/design_refs/support.js
- network request — Outbound network request in design_handoff_openhire_v01/design_refs/support.js
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Agent-native job search over employer ATS APIs (Greenhouse/Lever/Ashby/Beisen/Moka) — 139 employers across US/EU/China incl. robotics & autonomous-driving. Ghost-job scoring; your résumé never touches the server. MCP server for Claude/Cursor.
OpenHire · 开聘
A job-search radar for your AI assistant — first-party listings, ghost jobs scored, and your résumé never touches our servers.
让 AI 助手替你盯岗的求职雷达 —— 一手职位、幽灵岗位打分,简历不经过我们的服务器。
Real terminal output — install from PyPI, download the public index, search. No account, no signup.
What your agent actually sees
You ask your assistant a question in plain language. It calls search_jobs, and every row comes
back carrying the employer's real posting date — so the agent can reason about staleness
instead of guessing.
You: Any senior Python roles that are actually still open? Skip the stale ones.
// one row from search_jobs — trimmed to the fields that matter here
{
"title": "Senior Python Engineer",
"company": "MongoDB",
"datePosted": "2026-03-31", // from the employer's ATS, not a board's refreshed label
"days_open": 166,
"ghost_score": 0.61, // pure f(relist_count, first_seen_at) — frozen by a test
"apply_channel":"https://boards.greenhouse.io/…", // straight to the employer
"verified_at": "2026-09-02T09:47:10Z"
}
Assistant: This one has been open 166 days with a ghost_score of 0.61 — I'd deprioritise it.
Here are four posted in the last three weeks instead…
ghost_score measures how long a posting has been open, not whether the employer still intends
to hire. A long-open role can equally mean "hard to fill". Treat it as a reason to ask, not a verdict.
An MCP server that turns your AI assistant (Claude, Cursor, Windsurf) into a private radar for
AI / Infra, autonomous-driving and embodied-AI jobs — pulled straight from 139 employers'
own career sites and public ATS APIs (Greenhouse / Lever / Ashby / 北森 Beisen / Moka), across
the US, Europe and China (Waymo, Figure, Zoox — and Unitree, XPeng, UBTECH, Mech-Mind…).
No account. No signup. No résumé upload. Ever.
Three things a job board won't do for you:
- Kills ghost-job noise. Every listing carries a
ghost_scoreaged off the employer's
real posting date — the "2 days ago" a board shows you can be 300 days old in the ATS. - Structural privacy, not a pinky-promise. There is no résumé field in the protocol; a CI
test fails the build if anyone adds one. Matching runs on your machine — only an anonymous
fingerprint reaches the server. - Ranking you can't buy. Order is a locked pure function of (match, freshness). No
sponsored slots, no bidding — the signature is frozen by a test.
This is the 「哨兵 / Sentinel」 reference implementation — seedesign_handoff_openhire_v01/README.md for the full protocol spec.
Quickstart — under a minute
# 1. Install (pipx keeps it isolated and puts `ohp` on your PATH)
pipx install openhire
# 2. Get a job index. Downloads the public snapshot (~25 MB), then runs one incremental
# crawl to refresh verified_at / delisting. The crawl is the slow part: it can run for
# 20+ minutes on a cold index and prints nothing while it works.
# Only needed for the CLI — `ohp serve` fetches the snapshot by itself on first start.
ohp bootstrap # 139 employers · ~16k live postings · no account
# 3. Use it directly…
ohp search --required-skills rust,k8s --remote --role-family engineering
ohp search --currency CNY --role-family engineering # e.g. CN autonomous-driving / robotics roles
# …or connect it to an MCP client:
ohp serve
Then point your MCP client at it — see Works with below.
Works with
All clients use the same MCP entry. The canonical, zero-install config (needs
uv) works in every MCP client:
{ "mcpServers": { "openhire": { "command": "uvx", "args": ["openhire@latest", "serve"] } } }
The server auto-downloads the public job snapshot on first run if the index is empty, soohp bootstrap is optional. If you ran pipx install openhire, "command": "ohp" works too.
Claude Desktop — %APPDATA%\Claude\claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/); quit & reopen after editing:
{ "mcpServers": { "openhire": { "command": "ohp", "args": ["serve"] } } }
Cursor — ~/.cursor/mcp.json (or a project .cursor/mcp.json):
{ "mcpServers": { "openhire": { "command": "uvx", "args": ["openhire", "serve"] } } }
Windsurf — ~/.codeium/windsurf/mcp_config.json:
{ "mcpServers": { "openhire": { "command": "uvx", "args": ["openhire", "serve"] } } }
First start downloads the ~25 MB public snapshot (jobs/companies only) — give it a moment.
To refresh later runohp bootstrap --forceorohp ingest. On Windows Claude Desktop from
the Microsoft Store, the config is under…\Packages\<Claude package>\LocalCache\Roaming\Claude\.Hosted / remote:
ohp serve --transport streamable-http --host 0.0.0.0 --port 8000
exposeshttp://host:8000/mcp(also--transport sse). ADockerfileis included.
What it does
| Tool | What it gives you |
|---|---|
search_jobs |
Hard-filter the live index; every result carries verified_at, datePosted, days_open, ghost_score, remote_scope, eligible_regions, apply_channel. Filter by required_skills (AND), role_family, remote_scope, min_salary + currency. |
watch_intent |
Register a standing intent once — new matching jobs are waiting next time you check, even after you close the terminal. Accepts required_skills / role_family so sales / solutions roles stay out. |
check_watches |
Pull the matches that are new since your last check (client-pull; stdio has no push). |
authorize_application |
One explicit confirmation per job. It records your authorization and returns the employer's own application URL — you apply as yourself. It cannot accept a résumé. |
get_company_info |
Aggregate, anonymous trust signals for one employer (ghost_score_avg, active_jobs, index_built_at). Never any candidate data. |
Optional, entirely local: ohp init --scan <dir> derives a skill fingerprint from your
own repos. You never write a résumé; the code never leaves your machine — only an anonymous
vector does.
The five protocol fields
Every listing is valid schema.org/JobPosting, plus:
verified_at— last moment confirmed live on the employer's own sitesource—employer_site | ats_public_api(never a job board)ghost_score— 0–1 listing-activity signal, aged off the real posting date (lower =
fresher). A noise filter, not an accusation: long-open listings are often evergreen talent
pools or slow pipelines — the score simply lets agents down-rank low-activity noiseresponse_sla_days— employer's committed response window (v0.1: always null)apply_channel— always the employer's own application URL, deep-linked to the specific job
Privacy Policy
Short version: there is no résumé field in the protocol, matching runs on your machine, and
the only user-originated value the server ever stores is an anonymous client-generated
fingerprint. No analytics, no telemetry, no third-party sharing. Full policy:
docs/PRIVACY.md.
Privacy model
| Résumé / PII upload | never — matching runs locally; a résumé never transits the server, and we never store one |
| What the server sees | one anonymous, client-generated fingerprint + hard filters |
| Repo scan | local-only · personal projects · explicit consent · opt-out anytime |
| Job sources | first-party only: employer career pages + public ATS APIs (Greenhouse / Lever / Ashby) |
First-run data — the snapshot vs. fresh
ohp bootstrap (default) downloads a small public index snapshot (a GitHub Release
asset — companies + jobs only, zero user data) and then runs one incremental crawl to
refresh verified_at / delisting. --fresh skips the snapshot and crawls the public ATS from
scratch with the free offline heuristic extractor. Either way: no account, no PII.
Two things that surprise people:
- The incremental crawl is slow and quiet. On a cold index it can run for 20+ minutes
with no output. It is working, not hung. If you only want the data,ohp serveskips it
entirely — the server downloads the snapshot on first start and is answering in seconds. - The snapshot URL is pinned to the
v0.1.0tag on purpose. It looks stale; it is not.
That asset is overwritten in place every Monday by a scheduled workflow, so the URL is a
stable address for always-current data. Pinning it to the newest tag would break every
client the moment a release is cut.
Three rules this project will never break
- Your résumé stays on your machine — it never transits the server, and we never store it.
- Ranking is not for sale — it is only
f(match_quality, freshness), a locked pure function. - Employers pay only for authorized, delivered outcomes — never for exposure. (v0.1 has no
billing at all.)
These are enforced by CI (tests/test_privacy.py, tests/test_ranking.py,tests/test_snapshot.py).
Development
python -m venv .venv && . .venv/Scripts/activate # Windows
pip install -e ".[dev]"
pytest # privacy red lines + ranking + snapshot must be green
Set OPENHIRE_DATABASE_URL=postgresql+psycopg://… to run against Postgres instead of the
default local SQLite file (~/.openhire/openhire.db).
Roadmap
- v0.2 – v0.3 (shipped) — CN ATS adapters (北森 Beisen + Moka) · weekly auto-refreshed
public snapshot ·ghost_scorepublic beta · 139 employers across US / EU / China - next — Employer claim + verified badges — employers can reserve their claim
today via a
corporate-identity GitHub issue (zero-cost now; badges + listing-status control ship next) ·
response-SLA enforcement (7-day auto-delist) · redacted proof-of-fit — an anonymous,
candidate-authorized match summary that travels with an application (skills overlap only;
identity never included, résumés still never transit the server) - v1.0 — Open, vendor-neutral schema extension for AI-readable job postings
FAQ
Where does the job data come from?
Directly from 139 employers' own public ATS APIs (Greenhouse, Lever, Ashby, 北森 Beisen, Moka) — the
same endpoints that power their careers pages. No scraping, no third-party job boards. source is
always ats_public_api, and verified_at records the last time we confirmed each posting live.
The public index is auto-refreshed weekly, so a fresh ohp bootstrap starts from recent data.
Why should I trust ghost_score?
It's a pure, open, unpurchasable function — min(1, 0.15·relist_count + staleness) aged off the
real ATS posting date, not our crawl date. The formula lives in pipeline/ghost_score.py,
is unit-tested, and takes no money as input (red line #2). Long-open, repeatedly-relisted
postings score higher; you can always re-rank client-side. Read it as signal-to-noise, not
bad faith: plenty of high-scoring listings are legitimate evergreen talent pools. Employers
who want their listing activity represented accurately can claim their tenant (see Roadmap).
Does my résumé actually go through the server — really?
No. There is no résumé anywhere in the protocol. authorize_application has no résumé/file
parameter (it structurally cannot accept one), matching runs on your machine, and the only thing
that ever transits the server is a short anonymous fingerprint like #a3f9. This is enforced bytests/test_privacy.py, and the published snapshot carries zero user data (tests/test_snapshot.py).
Does it support China (中国区)?
Yes — this is what sets OpenHire apart. Employers on 北森 Beisen (<tenant>.zhiye.com) and
Moka (app.mokahr.com) are indexed: 20+ autonomous-driving / robotics / embodied-AI
companies including 宇树 Unitree, 小鹏 XPeng, 优必选 UBTECH, 梅卡曼德 Mech-Mind, 速腾聚创 RoboSense,
元戎启行 DeepRoute, 星海图 Galaxea, 傅利叶 Fourier, 普渡 Pudu. Pay published as 月薪 keeps its real
period (salary_period), so a salary floor no longer silently drops Chinese roles.
飞书招聘 (Feishu Hire) is not supported and won't be: it signs its job-list requests with a
ByteDance _signature and gates them behind a captcha SDK, so its listings are not publicly
readable. We don't break anti-bot measures.
How do I get a company added?
Open a Company inclusion request issue (title it with the company + its ATS URL) — this is
the best way to contribute. If you code, add it to src/openhire/seed/candidates.py (company
slug + ATS vendor/tenant) and open a PR; the seeder validates tenants against the live API.
License
MIT © OpenHire Protocol · PRs welcome.
Built by a non-coder PM-ing Claude Code — full acceptance reports in reports/.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found