hiring-mcp
Provides S3-compatible object storage for candidate resumes, agent config files, and session logs, with support for presigned PUT URLs and verified uploads.
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., "@hiring-mcplist open positions"
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.
hiring-mcp
A self-hosted hiring funnel where candidates apply through their own AI agent (Claude Code / Codex CLI) over the Model Context Protocol — plus the recruiter console to review them. Real Postgres, S3-compatible storage, LinkedIn SSO, and a Bearer-authed remote MCP endpoint.
How it works
careers site ──"Apply now"──▶ /jobs/:slug/apply ──▶ LinkedIn SSO ──▶ /mcp (token, once)
│
candidate's AI agent ══ Streamable HTTP + Bearer ══▶ POST /mcp/core (7 tools)
│
fills profile ▸ uploads resume ▸ compares resume↔JD ▸ records decision
│
recruiter console (/recruiter) ──▶ pipeline: submitted → under_review → interviewing → hired/rejectedA candidate lands on
/jobs/:jobId/applyfrom your careers page (jobId=external_job_idslug or internal uuid). The job survives the SSO round-trip.They sign in with LinkedIn (OIDC); a candidate account is found-or-created and a personal MCP Bearer token is shown once on
/mcp(reissue anytime).They connect their agent (
claude mcp add --transport http …). The agent readsget_my_profile— which includes the target position + full JD — fills the profile, uploads the resume, then presents a resume↔JD fit comparison.The candidate decides.
decision: "apply"→ application submitted;decision: "decline"→ recorded as declined (the agent must tell the candidate a decline is still visible to the hiring team). One application per role, enforced by a DB constraint. Fit score/summary/gaps are stored as the candidate's self-assessment — labeled that way everywhere they're shown.Recruiters work the pipeline in
/recruiter: filterable application list with legal status transitions, full candidate review (profile, rendered resume, session logs), and positions management (create/close/reopen). Optional webhook (APPLICATION_WEBHOOK_URL) pings on every new application.
Related MCP server: AI Applyd
The MCP tools (7, +2 behind a flag)
Tool | Purpose |
| Profile, resume, readiness + |
| Patch profile fields. Email is identity: settable once, immutable after |
| Resume as markdown (≤25k chars, versioned history) |
| Open roles / full JD |
| Record decision ( |
| Status of the candidate's applications |
| Only when |
Gating: application_ready = complete profile + resume (+ confirmed session log when
the flag is on). transformative_books requires a substantive (≥200 char) answer in the
candidate's own voice.
Quick start
cp .env.example .env # defaults match docker-compose (mock SSO on)
npm install
npm run infra:up # Postgres + MinIO (+ bucket) via Docker
npm run db:migrate
npm run db:seed # 3 sample positions
npm run dev # http://localhost:8787Then either open http://localhost:8787/jobs/ai-agent-engineer/apply and walk the
candidate flow (mock SSO lets you sign in as any test identity), or run the full
end-to-end check from another terminal:
npm run smoke # mints a candidate, drives the whole MCP apply flow
npm test # unit tests (validation, sessions, SSO linking, pipeline, XSS guards)Recruiter console: http://localhost:8787/recruiter — sign in with ADMIN_TOKEN.
Connect an AI agent
claude mcp add --transport http hiring https://your-host/mcp/core \
--header "Authorization: Bearer <candidate-token>"Architecture
Transport: stateless Streamable HTTP at
POST /mcp/core. Every request authenticates by Bearer token (SHA-256 hash stored, TTL-enforced), resolves the candidate, and gets a fresh per-candidate server instance — any compute node can serve any request.Web: zero-framework server-rendered HTML (candidate portal + recruiter console share one design system). Sessions are HMAC-signed cookies with server-side expiry.
DB: Postgres via Drizzle (
src/db/schema.ts). Application statuses:submitted | declined | under_review | interviewing | rejected | hired, with legal transitions enforced insrc/services/pipeline.ts.Storage: S3-compatible (MinIO locally). Session-log uploads go direct to S3 via presigned PUT and are verified by re-downloading and recomputing the SHA-256 before promotion.
Security posture
Candidate tokens: 24-byte random, hash-at-rest, 90-day TTL, one-time reveal, self-service rotation.
Mock SSO (
LINKEDIN_MOCK) is an explicit opt-in and refused in production.SSO email-linking only on verified OIDC emails into rows with no existing LinkedIn identity; profile email is immutable once set (prevents email-squatting takeover).
Production boot refuses default/weak
ADMIN_TOKEN/SESSION_SECRET; admin compare is constant-time; rate limits on every credential-accepting route; security headersCSP; candidate URLs scheme-checked at write and render (no
javascript:hrefs).
Surfaces
Path | Who | What |
| candidate's agent | MCP endpoint (Bearer) |
| candidate | portal (cookie) |
| public | apply entry + shareable JD |
| recruiter | console (admin-token login → 12h cookie) |
| ops/scripts | JSON API (Bearer |
Project layout
src/
config.ts env config + production safety checks
auth.ts token hashing, candidate lookup, mint, safeEqual
validation.ts resume / books rules, isUuid
auth/linkedin.ts OIDC flow + account-linking policy (planLink)
db/ schema, connection, migrate, seed
services/ readiness (apply gate), pipeline (status transitions),
storage (S3 presign/verify), notify (webhook)
mcp/ per-candidate server + tools (profile, positions, sessionLog)
web/ candidate portal: routes, views, session, http hardening
recruiter/ recruiter console: routes, views, session
admin/routes.ts JSON API for ops/scripts
scripts/ mint-token, smoke (e2e)
test/ unit tests (vitest)Production notes
NODE_ENV=productionactivates the boot-time secret checks, secure cookies, and the mock-SSO refusal. Put TLS in front;trust proxyis already set to 1 hop.Container:
docker build -t hiring-mcp .— runs migrations separately (node dist/db/migrate.js) before rollout, never at boot.The in-memory rate limiter is single-process by design; move to a shared store if you ever run multiple instances. See
DEPLOY.mdfor the serverless blueprint (deliberately deferred until traffic justifies it).LinkedIn: create an app at developer.linkedin.com with Sign In with LinkedIn using OpenID Connect, redirect URI
<PUBLIC_BASE_URL>/auth/linkedin/callback, then setLINKEDIN_CLIENT_ID/LINKEDIN_CLIENT_SECRETandLINKEDIN_MOCK=false.
This server cannot be deployed
Maintenance
Related MCP Connectors
Job application tracker for developers - AI agents write over MCP, you review in a dashboard.
AI resume triage for recruiters. Query your candidate pool from Claude or ChatGPT.
Deploy and manage your apps, databases, storage, and scheduled jobs from your AI agent
AI agent recruiting: talent pool match, reference checks, credit packs via Stripe MPP
Related MCP Servers
AlicenseBqualityFmaintenanceOfficial Model Context Protocol server for 100Hires — the applicant tracking system for recruiting teams. Exposes the full 100Hires API v2 as 130 MCP tools, enabling AI assistants to manage candidates, jobs, applications, interviews, messages, and more.10012 npm1MIT- AlicenseAqualityAmaintenanceAuto-Apply That Ends on an Interview. Job matches, applications submitted on the employer's own hiring system automatically or after your approval, employer-receipt tracking, a resume rewritten for each role, cover letters and interview prep. Hosted remote server over Streamable HTTP with OAuth 2.1.17429 npm2MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to search, score, and track job applications from ATS boards via MCP, with explainable matching and an append-only application history.MIT

Civify MCP Serverofficial
AlicenseAqualityBmaintenanceEnables AI agents to parse resumes, tailor CVs, score ATS compatibility, redact PII, scrape job postings, and track applications through a career platform.855 npmMIT