hirelayer-mcp 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/client.js +2 -0
- package/dist/server.js +18 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ The official [Model Context Protocol](https://modelcontextprotocol.io) server fo
|
|
|
41
41
|
## What you can do
|
|
42
42
|
|
|
43
43
|
- **Parse resumes and CVs** (Resume Parser). Turn PDF, Word, image and other files into structured JSON: contact details, work experience, education, languages, skills and the full text. Scanned resumes go through OCR, and the resume language is detected.
|
|
44
|
-
- **Turn a job description into criteria** (Job Criteria Extractor). Get typed, weighted criteria, each marked required, preferred or bonus. Free.
|
|
44
|
+
- **Turn a job description into criteria** (Job Criteria Extractor). Get the typed, weighted criteria the posting states, each marked required, preferred or bonus, in English, French, German, Spanish or Portuguese. Free.
|
|
45
45
|
- **Score and rank resumes** (Resume Scorer). Score up to 50 resumes against a job in one call, from 0 to 1, with a verdict, a summary and the evidence for each criterion, best first. Ask for a precise ranking to re-rank the 10 best with a reason each.
|
|
46
46
|
- **Search your talent pool** (Talent Pool Search). Find the resumes already in your HireLayer database closest to a job, around a city if you like.
|
|
47
47
|
- **Recommend jobs** (Job Recommendations). Find your open jobs closest to a candidate.
|
|
@@ -218,8 +218,8 @@ In Docker, `parse_resume` only reads files that you mount into the container. Ot
|
|
|
218
218
|
| Tool | Product | What it does | Typical input |
|
|
219
219
|
|---|---|---|---|
|
|
220
220
|
| `parse_resume` | Resume Parser | Parses a resume or CV into structured JSON: contact details, experience, education, languages, skills and the full text, stored under an `application_id` | A local `file_path`, a public `file_url` or the `resume_text`. Accepts PDF, DOC, DOCX, ODT, RTF, TXT, HTML, Markdown, PPT, PPTX, ODP, XLS, JPG, PNG or BMP files under 4.5 MB. |
|
|
221
|
-
| `extract_job_criteria` | Job Criteria Extractor | Turns a job description into typed criteria, each with a requirement (required, preferred or bonus) and a weight from 0 to 3, and stores the job under a `job_id` | Job description text |
|
|
222
|
-
| `score_resumes` | Resume Scorer | Scores 1 to 50 resumes against one job, best first: a score from 0 to 1, a verdict, a summary, the required criteria missing and the evidence for each criterion. `precise_ranking` re-ranks the 10 best with a reason each | The job as `job_text` (optionally with your own `criteria`) or a `job_id`, and each resume as `text` or an `application_id
|
|
221
|
+
| `extract_job_criteria` | Job Criteria Extractor | Turns a job description into the typed criteria it states, each with a requirement (required, preferred or bonus) and a weight from 0 to 3, and stores the job under a `job_id` | Job description text, at least 30 characters |
|
|
222
|
+
| `score_resumes` | Resume Scorer | Scores 1 to 50 resumes against one job, best first: a score from 0 to 1, a verdict, a summary, the required criteria missing and the evidence for each criterion. `precise_ranking` re-ranks the 10 best with a reason each | The job as `job_text` (optionally with your own `criteria`) or a `job_id`, and each resume as `text` or an `application_id`; at least one resume of 30 characters or more |
|
|
223
223
|
| `search_resumes` | Talent Pool Search | Finds the stored resumes closest to a job, with optional city, radius and date filters. Proximity only: score the ones worth it with `score_resumes` | A `job_id` or `job_text` |
|
|
224
224
|
| `search_jobs` | Job Recommendations | Finds the stored open jobs closest to a resume. Proximity only | An `application_id` or `resume_text` |
|
|
225
225
|
| `normalize_skills` | Skills Normalizer | Maps free-text skills to taxonomy skills, with their families and domains, named in French or English | Free text, from one skill to a whole skills section |
|
|
@@ -296,7 +296,7 @@ Trimmed to one resume and one criterion:
|
|
|
296
296
|
}
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
-
Criteria, summaries and explanations are written in the job's language (French or English) unless you pass `language`; evidence quotes the resume in its own language.
|
|
299
|
+
Criteria, summaries and explanations are written in the job's language (English, French, German, Spanish or Portuguese; English for any other) unless you pass `language`; evidence quotes the resume in its own language.
|
|
300
300
|
|
|
301
301
|
</details>
|
|
302
302
|
|
|
@@ -382,7 +382,7 @@ HIRELAYER_API_KEY=your-api-key npx @modelcontextprotocol/inspector npx -y hirela
|
|
|
382
382
|
No. HireLayer provides the AI building blocks of recruiting software: resume parsing, scoring and ranking, talent pool search and skills. Use them on their own through MCP, or plug them into your ATS or HR tech product through the [REST API](https://hirelayer.co/api-docs).
|
|
383
383
|
|
|
384
384
|
**Which language are the results in?**
|
|
385
|
-
The text that HireLayer writes (criteria, summaries, explanations and ranking reasons) is in the job's language, French or
|
|
385
|
+
The text that HireLayer writes (criteria, summaries, explanations and ranking reasons) is in the job's language: English, French, German, Spanish or Portuguese, and English for a job written in another language. Choose it with `language` (`en`, `fr`, `de`, `es` or `pt`). Evidence quotes the resume in its own language. Skills normalization returns French or English labels.
|
|
386
386
|
|
|
387
387
|
**Which resume languages are supported?**
|
|
388
388
|
The parser detects the main language of each resume and returns it in `resume.info_resume.language`.
|
package/dist/client.js
CHANGED
|
@@ -34,6 +34,8 @@ export class HireLayerError extends Error {
|
|
|
34
34
|
this.name = 'HireLayerError';
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
/** The languages a job's criteria, summaries and explanations are written in. */
|
|
38
|
+
export const JOB_LANGUAGES = ['en', 'fr', 'de', 'es', 'pt'];
|
|
37
39
|
export class HireLayerClient {
|
|
38
40
|
apiKey;
|
|
39
41
|
baseUrl;
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { HireLayerError, VERSION } from './client.js';
|
|
3
|
+
import { HireLayerError, JOB_LANGUAGES, VERSION } from './client.js';
|
|
4
4
|
// Clients drop calls that stay silent too long (often after 60 s); parsing takes about
|
|
5
5
|
// 30 s, scans needing OCR and scoring 50 resumes longer, so long calls report progress at this interval.
|
|
6
6
|
const PROGRESS_INTERVAL_MS = 10_000;
|
|
@@ -19,7 +19,8 @@ Resume input:
|
|
|
19
19
|
- score_resumes and search_jobs take resume text directly: when you can already read the resume, pass its text instead of parsing it first. For a resume file, parse it with parse_resume and pass the application_id it returns.
|
|
20
20
|
|
|
21
21
|
Notes:
|
|
22
|
-
- Criteria, summaries and explanations are written in the job's language (French or English) unless language says otherwise; evidence quotes the resume in its own language.
|
|
22
|
+
- Criteria, summaries and explanations are written in the job's language (English, French, German, Spanish or Portuguese; English for any other) unless language says otherwise; evidence quotes the resume in its own language.
|
|
23
|
+
- A job text holds at least 30 characters, and score_resumes needs at least one resume of 30 characters or more.
|
|
23
24
|
- Job and resume texts are accepted up to 50,000 characters each: truncate longer ones. score_resumes takes up to 50 resumes per call.
|
|
24
25
|
- HireLayer keeps the jobs, resumes and scores in the user's account, as the REST API does, unless the workspace runs without storage. A pair already scored is not evaluated again and costs nothing.
|
|
25
26
|
- Pass do_not_store_data: true to parse_resume when the user does not want HireLayer to keep the resume (Enterprise plan). Such a resume gets no application_id: score it by its text.
|
|
@@ -45,10 +46,15 @@ const workspaceArg = z
|
|
|
45
46
|
.optional()
|
|
46
47
|
.describe(`The client company this call belongs to, when the user recruits for several companies: its HireLayer workspace id (${CLIENT_ID_RULE}), e.g. acme-corp. Jobs, resumes and scores stay apart from other clients'. A new id creates a new workspace, so reuse the exact id. Omit it for the user's default workspace.`);
|
|
47
48
|
const languageArg = z
|
|
48
|
-
.enum(
|
|
49
|
+
.enum(JOB_LANGUAGES)
|
|
49
50
|
.optional()
|
|
50
|
-
.describe("Language of the criteria, summaries and explanations: fr or
|
|
51
|
-
|
|
51
|
+
.describe("Language of the criteria, summaries and explanations: en, fr, de, es or pt. Defaults to the language of the job's text (English when it is another).");
|
|
52
|
+
// The API refuses a job text under 30 characters.
|
|
53
|
+
const jobText = z
|
|
54
|
+
.string()
|
|
55
|
+
.trim()
|
|
56
|
+
.min(30, 'A job text holds at least 30 characters: send the full job description.')
|
|
57
|
+
.max(50000);
|
|
52
58
|
const resumeText = z.string().min(1).max(50000);
|
|
53
59
|
// The format extract_job_criteria returns; criterion_id and source, sent back, are ignored by the API.
|
|
54
60
|
const criterion = z.object({
|
|
@@ -122,9 +128,9 @@ export function createServer(client) {
|
|
|
122
128
|
}, ({ file_path, file_url, resume_text, application_id, do_not_store_data, workspace }, extra) => run(extra, (signal) => client.parseResume({ filePath: file_path, fileUrl: file_url, text: resume_text }, { applicationId: application_id, doNotStoreData: do_not_store_data, workspace, signal })));
|
|
123
129
|
server.registerTool('extract_job_criteria', {
|
|
124
130
|
title: 'Job Criteria Extractor',
|
|
125
|
-
description:
|
|
131
|
+
description: "Turn a job description (any language, at least 30 characters) into the typed criteria it states (usually 4 to 10, fewer for a short posting), in the posting's order, each with a criterion_id, a label, a type, a requirement (required, preferred or bonus), a weight from 0 to 3 and an optional threshold. They are written in the job's language (English, French, German, Spanish or Portuguese; English for any other) unless language says otherwise. The job is stored under job_id, so score_resumes can reuse it. Free, except a new text sent under the job_id of a job already scored: its scores are evaluated again, 1 credit each.",
|
|
126
132
|
inputSchema: {
|
|
127
|
-
job_text: jobText.describe('Full job description, as published.'),
|
|
133
|
+
job_text: jobText.describe('Full job description, as published: at least 30 characters.'),
|
|
128
134
|
title: z.string().max(300).optional().describe('Job title.'),
|
|
129
135
|
location: z.string().max(300).optional().describe('Where the job is based.'),
|
|
130
136
|
language: languageArg,
|
|
@@ -135,9 +141,9 @@ export function createServer(client) {
|
|
|
135
141
|
}, ({ job_text, title, location, language, job_id, workspace }, extra) => run(extra, (signal) => client.extractJobCriteria({ text: job_text, title, location, language, job_id }, { workspace, signal })));
|
|
136
142
|
server.registerTool('score_resumes', {
|
|
137
143
|
title: 'Resume Scorer',
|
|
138
|
-
description:
|
|
144
|
+
description: "Evaluate 1 to 50 resumes against a job, criterion by criterion, and return them best first: for each, a score between 0 and 1, a verdict (highly_relevant, relevant, marginally_relevant or not_relevant), a summary, the required criteria missing or to verify, and the evidence for each criterion. Give the job as job_text (its criteria are extracted on the way, or pass your own criteria) or as the job_id of a stored job. Give each resume as text or as the application_id of a stored resume; at least one resume of the call holds 30 characters or more. Summaries and explanations are written in the job's language. With precise_ranking, the 10 best are re-ranked against each other, each with a ranking_reason. The result is decision support for a human recruiter, not an automated hiring decision. Costs 1 HireLayer credit per resume evaluated, and 1 more with precise_ranking; a pair already scored costs 0.",
|
|
139
145
|
inputSchema: {
|
|
140
|
-
job_text: jobText.optional().describe('The job description. Give job_text or job_id.'),
|
|
146
|
+
job_text: jobText.optional().describe('The job description, at least 30 characters. Give job_text or job_id.'),
|
|
141
147
|
job_id: JOB_ID_RULE.optional().describe('Alone: a job already stored, such as the job_id extract_job_criteria returned. With job_text: store the job under your own id; a new text or new criteria for a job already scored evaluate its stored scores again, 1 credit each.'),
|
|
142
148
|
criteria: z
|
|
143
149
|
.array(criterion)
|
|
@@ -145,7 +151,7 @@ export function createServer(client) {
|
|
|
145
151
|
.max(20)
|
|
146
152
|
.optional()
|
|
147
153
|
.describe("The job's own criteria, in the format extract_job_criteria returns; they replace the extraction. Only with job_text."),
|
|
148
|
-
language: languageArg.describe("Language of the criteria, summaries and explanations: fr or
|
|
154
|
+
language: languageArg.describe("Language of the criteria, summaries and explanations: en, fr, de, es or pt. Only with job_text; defaults to the language of the job's text (English when it is another)."),
|
|
149
155
|
resumes: z
|
|
150
156
|
.array(z.object({
|
|
151
157
|
text: resumeText.optional().describe('The resume as plain text.'),
|
|
@@ -177,10 +183,10 @@ export function createServer(client) {
|
|
|
177
183
|
}));
|
|
178
184
|
server.registerTool('search_resumes', {
|
|
179
185
|
title: 'Talent Pool Search',
|
|
180
|
-
description: "Find the resumes of the user's resume database closest to a job, by proximity only: application_ids with a similarity between 0 and 1, best first, and whether each is already scored for the job. No evaluation: pass the ones worth it to score_resumes. Give the job as the job_id of a stored job or as job_text (the job is then stored). Costs 1 HireLayer credit per search.",
|
|
186
|
+
description: "Find the resumes of the user's resume database closest to a job, by proximity only: application_ids with a similarity between 0 and 1, best first, and whether each is already scored for the job. No evaluation: pass the ones worth it to score_resumes. Give the job as the job_id of a stored job or as job_text (at least 30 characters; the job is then stored). Costs 1 HireLayer credit per search.",
|
|
181
187
|
inputSchema: {
|
|
182
188
|
job_id: JOB_ID_RULE.optional().describe('A stored job. Give job_id or job_text.'),
|
|
183
|
-
job_text: jobText.optional().describe('A job description not stored yet.'),
|
|
189
|
+
job_text: jobText.optional().describe('A job description not stored yet, at least 30 characters.'),
|
|
184
190
|
limit: z.number().int().min(1).max(500).optional().describe('How many resumes to return, 50 by default.'),
|
|
185
191
|
city: z
|
|
186
192
|
.string()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hirelayer-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"mcpName": "co.hirelayer/hirelayer",
|
|
5
5
|
"description": "Official HireLayer MCP server: resume and CV parsing, job criteria extraction, resume scoring and ranking, talent pool search, job recommendations and skills normalization for AI agents in Claude, Cursor, VS Code and other MCP clients.",
|
|
6
6
|
"type": "module",
|