A recruitment-focused MCP server that provides tools for job searching, candidate scoring, skill extraction, and candidate rejection with MRTR confirmation. Demonstrates MCP Tasks extension, stateless HTTP transport, and multi-region support.
Strong foundation with clear naming, comprehensive descriptions, and well-structured schemas. All 6 tools follow verb_noun convention (search_, get_, extract_, score_, reject_, bulk_score_). Descriptions are detailed (100-250 chars) and explain WHAT, WHEN, and consequences. Input schemas use proper JSON Schema with types and descriptions. Key strengths: pagination support (search_jobs), determinism guarantees, MRTR confirmation pattern (reject_candidate), task-based async (bulk_score_shortlist). Gaps: output schemas not explicitly documented in source; some parameter descriptions could be more prescriptive about constraints; no enum constraints visible for seniority/location despite being enumerated values.
Scores every candidate in a shortlist against one job posting, using the same explainable scoring as score_candidate_fit. Runs as an MCP task: the client must declare the Tasks extension, and should poll tasks/get with the returned task id rather than waiting on this call. Candidates that do not exist are reported in unmatchedCandidateIds rather than silently dropped. Deterministic — no language model is involved.
Normalizes skills against the taxonomy and returns their canonical ids. Deterministic: the same input always yields the same output, and no language model is involved. Supply exactly one of text (a CV or job description to scan) or names (an already-separated list). With names, entries the taxonomy does not know are reported in unrecognisedNames rather than silently dropped.
Reads one job posting in full, including its description. Use search_jobs to find ids. The read is scoped by the Region header when the client sends one: a posting belonging to another region is reported as not served here rather than returned.
Rejects a candidate and records why. DESTRUCTIVE and confirmation-gated: the first call answers input_required, asking the user to confirm and — if none was supplied — to state a reason. Call again with the returned requestState and the user's answer in inputResponses. The reason is carried inside requestState, so it cannot be changed between the confirmation and the write. A reason of at least 10 characters is required either way.
Output schemas not documented in source code. Tool descriptions explain what is returned (e.g., 'paginated results', 'per-component breakdown') but formal JSON Schema output definitions are not visible. LLMs cannot reliably extract fields from undocumented responses.
Seniority and location parameters accept free-form strings but are documented as accepting only specific values (Junior/Mid/Senior, ISO 3166-1 alpha-2). No enum constraint visible in schema. LLMs may pass invalid values like 'senior' or 'US' instead of 'Senior' or 'US'.
reject_candidate requires minimum 10-character reason but this constraint is not expressed in JSON Schema (no minLength). Parameter description states the rule in prose, but LLMs cannot validate constraints from text alone.
Inferred effective spec: 2026-07-28+.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 61 | 2026-07-28+ | v2 |
Scores one candidate against one job posting and returns a 0-100 total with a per-component breakdown: skill overlap, seniority distance and location compatibility, each with its own raw score, weight and a reason code. Deterministic — no language model is involved, so the same pair always scores the same.
Searches job postings by skill requirements, seniority level, and location. Returns paginated results with opaque handles for fetching subsequent pages. Deterministic — no language model is involved.
bulk_score_shortlist requires Tasks extension (SEP-1686) but no evidence in source that Tasks capability is declared or negotiated. If client does not support Tasks, tool will fail silently or return confusing error.
Error handling descriptions are minimal. Tools state what can go wrong (e.g., 'posting not served here', 'unrecognised names') but do not guide LLM recovery (e.g., 'try search_jobs with different filters', 'call extract_skills with text instead of names').