@theyahia/hh-mcp 1.1.0 → 2.0.1
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/LICENSE +21 -21
- package/README.md +94 -41
- package/dist/client.d.ts +6 -0
- package/dist/client.js +64 -9
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +52 -19
- package/dist/index.js.map +1 -1
- package/dist/tools/__tests__/tools.test.d.ts +1 -0
- package/dist/tools/__tests__/tools.test.js +181 -0
- package/dist/tools/__tests__/tools.test.js.map +1 -0
- package/dist/tools/employers.d.ts +27 -2
- package/dist/tools/employers.js +38 -5
- package/dist/tools/employers.js.map +1 -1
- package/dist/tools/references.d.ts +19 -5
- package/dist/tools/references.js +30 -10
- package/dist/tools/references.js.map +1 -1
- package/dist/tools/resumes.d.ts +9 -3
- package/dist/tools/resumes.js +30 -21
- package/dist/tools/resumes.js.map +1 -1
- package/dist/tools/salary.d.ts +12 -0
- package/dist/tools/salary.js +24 -0
- package/dist/tools/salary.js.map +1 -0
- package/dist/tools/vacancies.d.ts +26 -3
- package/dist/tools/vacancies.js +61 -9
- package/dist/tools/vacancies.js.map +1 -1
- package/dist/types.d.ts +50 -0
- package/package.json +36 -8
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 theYahia
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 theYahia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
# @theyahia/hh-mcp
|
|
2
2
|
|
|
3
|
-
MCP
|
|
3
|
+
MCP server for the [hh.ru](https://hh.ru) API — Russia and CIS job market. **16 tools** covering vacancies, resumes, employers, salary statistics, dictionaries, and autocomplete.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@theyahia/hh-mcp)
|
|
6
6
|
[](https://github.com/theYahia/hh-mcp/actions)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Part of the [Russian API MCP](https://github.com/theYahia/russian-mcp) series by [@theYahia](https://github.com/theYahia).
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Two Modes
|
|
12
12
|
|
|
13
|
-
|
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
|
13
|
+
| Mode | What's available | Token needed? |
|
|
14
|
+
|------|-----------------|:-------------:|
|
|
15
|
+
| **No token** | Vacancy search, vacancy by ID, similar vacancies, employers, salary stats, areas, roles, dictionaries, suggests | No |
|
|
16
|
+
| **With token** | Everything above + resume search, resume by ID | Yes (`HH_ACCESS_TOKEN`) |
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Get a token at [dev.hh.ru/admin](https://dev.hh.ru/admin).
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## Installation
|
|
21
21
|
|
|
22
22
|
### Claude Desktop
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ MCP-сервер для hh.ru: поиск вакансий, резюме, зар
|
|
|
28
28
|
"command": "npx",
|
|
29
29
|
"args": ["-y", "@theyahia/hh-mcp"],
|
|
30
30
|
"env": {
|
|
31
|
-
"HH_ACCESS_TOKEN": "
|
|
31
|
+
"HH_ACCESS_TOKEN": "optional-oauth-token"
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -39,63 +39,115 @@ MCP-сервер для hh.ru: поиск вакансий, резюме, зар
|
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
claude mcp add hh -- npx -y @theyahia/hh-mcp
|
|
42
|
-
#
|
|
42
|
+
# With token:
|
|
43
43
|
claude mcp add hh -e HH_ACCESS_TOKEN=your-token -- npx -y @theyahia/hh-mcp
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
### VS Code / Cursor
|
|
47
47
|
|
|
48
48
|
```json
|
|
49
|
-
{
|
|
49
|
+
{
|
|
50
|
+
"servers": {
|
|
51
|
+
"hh": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "@theyahia/hh-mcp"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
50
57
|
```
|
|
51
58
|
|
|
52
59
|
### Windsurf
|
|
53
60
|
|
|
54
61
|
```json
|
|
55
|
-
{
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"hh": {
|
|
65
|
+
"command": "npx",
|
|
66
|
+
"args": ["-y", "@theyahia/hh-mcp"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
56
70
|
```
|
|
57
71
|
|
|
58
|
-
### HTTP
|
|
72
|
+
### HTTP Mode (Streamable HTTP)
|
|
59
73
|
|
|
60
74
|
```bash
|
|
61
75
|
npx @theyahia/hh-mcp --http
|
|
62
|
-
#
|
|
76
|
+
# or
|
|
63
77
|
HTTP_PORT=8080 npx @theyahia/hh-mcp --http
|
|
64
78
|
```
|
|
65
79
|
|
|
66
|
-
|
|
80
|
+
Endpoint: `http://localhost:3000/mcp`
|
|
67
81
|
Health check: `http://localhost:3000/health`
|
|
68
82
|
|
|
69
|
-
##
|
|
83
|
+
## Environment Variables
|
|
84
|
+
|
|
85
|
+
| Variable | Required | Description |
|
|
86
|
+
|----------|----------|-------------|
|
|
87
|
+
| `HH_ACCESS_TOKEN` | No | OAuth 2.0 Bearer token. Required for resume endpoints. |
|
|
88
|
+
| `HTTP_PORT` | No | Port for HTTP mode (default: 3000). |
|
|
89
|
+
|
|
90
|
+
## Tools (16)
|
|
91
|
+
|
|
92
|
+
### Vacancies
|
|
70
93
|
|
|
71
|
-
|
|
|
72
|
-
|
|
73
|
-
| `search_vacancies` |
|
|
74
|
-
| `get_vacancy` |
|
|
75
|
-
| `
|
|
76
|
-
| `get_resume` | Полная информация о резюме | **Да** |
|
|
77
|
-
| `get_employers` | Поиск работодателей по названию | Нет |
|
|
78
|
-
| `get_salary_stats` | Статистика зарплат по специальности и региону | Нет |
|
|
79
|
-
| `get_areas` | Справочник регионов РФ и СНГ с кодами | Нет |
|
|
80
|
-
| `get_professional_roles` | Справочник профессиональных ролей | Нет |
|
|
94
|
+
| Tool | Description | Token? |
|
|
95
|
+
|------|-------------|:------:|
|
|
96
|
+
| `search_vacancies` | Search vacancies by keywords, region, salary, experience, employment type, schedule, with sorting and pagination | No |
|
|
97
|
+
| `get_vacancy` | Full vacancy details: description, requirements, key skills, contacts | No |
|
|
98
|
+
| `get_similar_vacancies` | Find vacancies similar to a given one | No |
|
|
81
99
|
|
|
82
|
-
|
|
100
|
+
### Resumes
|
|
101
|
+
|
|
102
|
+
| Tool | Description | Token? |
|
|
103
|
+
|------|-------------|:------:|
|
|
104
|
+
| `search_resumes` | Search candidate resumes by keywords, region, role, salary, experience | **Yes** |
|
|
105
|
+
| `get_resume` | Full resume: experience, education, skills, contacts | **Yes** |
|
|
106
|
+
|
|
107
|
+
### Employers
|
|
108
|
+
|
|
109
|
+
| Tool | Description | Token? |
|
|
110
|
+
|------|-------------|:------:|
|
|
111
|
+
| `search_employers` | Search companies by name and region | No |
|
|
112
|
+
| `get_employer` | Employer profile: description, industries, website, vacancy count | No |
|
|
113
|
+
| `get_employer_vacancies` | List active vacancies for a specific employer | No |
|
|
114
|
+
|
|
115
|
+
### Dictionaries & Suggests
|
|
116
|
+
|
|
117
|
+
| Tool | Description | Token? |
|
|
118
|
+
|------|-------------|:------:|
|
|
119
|
+
| `get_areas` | Full tree of regions and cities with codes | No |
|
|
120
|
+
| `get_professional_roles` | Full tree of professional roles with IDs | No |
|
|
121
|
+
| `get_dictionaries` | All reference data: currencies, employment types, schedules, experience levels | No |
|
|
122
|
+
| `suggest_positions` | Autocomplete job titles | No |
|
|
123
|
+
| `suggest_companies` | Autocomplete company names | No |
|
|
124
|
+
| `suggest_areas` | Autocomplete region/city names | No |
|
|
125
|
+
|
|
126
|
+
### Salary
|
|
127
|
+
|
|
128
|
+
| Tool | Description | Token? |
|
|
129
|
+
|------|-------------|:------:|
|
|
130
|
+
| `get_salary_statistics` | Salary distribution for a professional role in a region | No |
|
|
131
|
+
|
|
132
|
+
## Rate Limiting
|
|
133
|
+
|
|
134
|
+
Built-in rate limiter respects the hh.ru API limit of 5 requests per second. Automatic retry with exponential backoff on 429 and 5xx errors (up to 3 attempts).
|
|
135
|
+
|
|
136
|
+
## Demo Prompts
|
|
83
137
|
|
|
84
138
|
```
|
|
85
|
-
|
|
86
|
-
Покажи вакансии в Яндексе
|
|
87
|
-
Средняя зарплата Senior Backend?
|
|
88
|
-
Какие регионы есть в hh.ru?
|
|
89
|
-
Найди резюме Java-разработчиков в Петербурге
|
|
139
|
+
Find remote Python developer jobs in Moscow paying over 300,000 RUB
|
|
90
140
|
```
|
|
91
141
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
142
|
+
```
|
|
143
|
+
Show me all open vacancies at Yandex and give me salary statistics for their top roles
|
|
144
|
+
```
|
|
95
145
|
|
|
96
|
-
|
|
146
|
+
```
|
|
147
|
+
Compare Senior Backend salaries in Moscow vs Saint Petersburg, and suggest similar vacancies to the best-paying one
|
|
148
|
+
```
|
|
97
149
|
|
|
98
|
-
##
|
|
150
|
+
## Development
|
|
99
151
|
|
|
100
152
|
```bash
|
|
101
153
|
git clone https://github.com/theYahia/hh-mcp.git
|
|
@@ -105,10 +157,11 @@ npm run build
|
|
|
105
157
|
npm test
|
|
106
158
|
```
|
|
107
159
|
|
|
108
|
-
##
|
|
160
|
+
## API Reference
|
|
109
161
|
|
|
110
|
-
|
|
162
|
+
- [hh.ru API docs](https://api.hh.ru/)
|
|
163
|
+
- [hh.ru API GitHub](https://github.com/hhru/api)
|
|
111
164
|
|
|
112
|
-
##
|
|
165
|
+
## License
|
|
113
166
|
|
|
114
167
|
MIT
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -1,14 +1,46 @@
|
|
|
1
1
|
const BASE_URL = "https://api.hh.ru";
|
|
2
2
|
const TIMEOUT = 10_000;
|
|
3
3
|
const MAX_RETRIES = 3;
|
|
4
|
-
const USER_AGENT = "theYahia-hh-mcp/
|
|
4
|
+
const USER_AGENT = "theYahia-hh-mcp/2.0 (https://github.com/theYahia/hh-mcp)";
|
|
5
|
+
// Rate limiter: 5 requests per second
|
|
6
|
+
const RATE_LIMIT = 5;
|
|
7
|
+
const RATE_WINDOW = 1000;
|
|
8
|
+
const timestamps = [];
|
|
9
|
+
async function waitForRateLimit() {
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
// Remove timestamps outside the window
|
|
12
|
+
while (timestamps.length > 0 && timestamps[0] <= now - RATE_WINDOW) {
|
|
13
|
+
timestamps.shift();
|
|
14
|
+
}
|
|
15
|
+
if (timestamps.length >= RATE_LIMIT) {
|
|
16
|
+
const oldest = timestamps[0];
|
|
17
|
+
const waitMs = oldest + RATE_WINDOW - now;
|
|
18
|
+
if (waitMs > 0) {
|
|
19
|
+
await new Promise((r) => setTimeout(r, waitMs));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
timestamps.push(Date.now());
|
|
23
|
+
}
|
|
24
|
+
export class HhApiError extends Error {
|
|
25
|
+
status;
|
|
26
|
+
statusText;
|
|
27
|
+
body;
|
|
28
|
+
constructor(status, statusText, body) {
|
|
29
|
+
super(`hh.ru HTTP ${status}: ${statusText}`);
|
|
30
|
+
this.status = status;
|
|
31
|
+
this.statusText = statusText;
|
|
32
|
+
this.body = body;
|
|
33
|
+
this.name = "HhApiError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
5
36
|
export async function hhGet(path) {
|
|
37
|
+
await waitForRateLimit();
|
|
6
38
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
7
39
|
const controller = new AbortController();
|
|
8
40
|
const timer = setTimeout(() => controller.abort(), TIMEOUT);
|
|
9
41
|
const headers = {
|
|
10
42
|
"User-Agent": USER_AGENT,
|
|
11
|
-
|
|
43
|
+
Accept: "application/json",
|
|
12
44
|
};
|
|
13
45
|
const token = process.env.HH_ACCESS_TOKEN;
|
|
14
46
|
if (token) {
|
|
@@ -22,23 +54,46 @@ export async function hhGet(path) {
|
|
|
22
54
|
clearTimeout(timer);
|
|
23
55
|
if (response.ok)
|
|
24
56
|
return response.json();
|
|
25
|
-
|
|
57
|
+
const body = await response.text().catch(() => "");
|
|
58
|
+
// Auth errors — fail immediately
|
|
59
|
+
if (response.status === 401 || response.status === 403) {
|
|
60
|
+
throw new HhApiError(response.status, response.status === 401
|
|
61
|
+
? "Unauthorized — set HH_ACCESS_TOKEN env var with a valid OAuth2 bearer token"
|
|
62
|
+
: "Forbidden — token lacks required scope for this endpoint", body);
|
|
63
|
+
}
|
|
64
|
+
// Rate limit — use Retry-After header if available
|
|
65
|
+
if (response.status === 429) {
|
|
66
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
67
|
+
const delay = retryAfter
|
|
68
|
+
? parseInt(retryAfter, 10) * 1000
|
|
69
|
+
: Math.min(1000 * 2 ** (attempt - 1), 8000);
|
|
70
|
+
if (attempt < MAX_RETRIES) {
|
|
71
|
+
console.error(`[hh-mcp] 429 rate limited, retry in ${delay}ms (${attempt}/${MAX_RETRIES})`);
|
|
72
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
throw new HhApiError(429, "Rate limited — all retries exhausted", body);
|
|
76
|
+
}
|
|
77
|
+
// Server errors — retry
|
|
78
|
+
if (response.status >= 500 && attempt < MAX_RETRIES) {
|
|
26
79
|
const delay = Math.min(1000 * 2 ** (attempt - 1), 8000);
|
|
27
|
-
console.error(`[hh-mcp] ${response.status},
|
|
28
|
-
await new Promise(r => setTimeout(r, delay));
|
|
80
|
+
console.error(`[hh-mcp] ${response.status}, retry in ${delay}ms (${attempt}/${MAX_RETRIES})`);
|
|
81
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
29
82
|
continue;
|
|
30
83
|
}
|
|
31
|
-
throw new
|
|
84
|
+
throw new HhApiError(response.status, response.statusText, body);
|
|
32
85
|
}
|
|
33
86
|
catch (error) {
|
|
34
87
|
clearTimeout(timer);
|
|
35
|
-
if (error instanceof DOMException &&
|
|
36
|
-
|
|
88
|
+
if (error instanceof DOMException &&
|
|
89
|
+
error.name === "AbortError" &&
|
|
90
|
+
attempt < MAX_RETRIES) {
|
|
91
|
+
console.error(`[hh-mcp] Timeout, retry (${attempt}/${MAX_RETRIES})`);
|
|
37
92
|
continue;
|
|
38
93
|
}
|
|
39
94
|
throw error;
|
|
40
95
|
}
|
|
41
96
|
}
|
|
42
|
-
throw new Error("hh.ru:
|
|
97
|
+
throw new Error("hh.ru: all retries exhausted");
|
|
43
98
|
}
|
|
44
99
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AACrC,MAAM,OAAO,GAAG,MAAM,CAAC;AACvB,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,UAAU,GAAG,0DAA0D,CAAC;AAE9E,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAY;IACtC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QAE5D,MAAM,OAAO,GAA2B;YACtC,YAAY,EAAE,UAAU;YACxB,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,39 +2,73 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
-
import { searchVacanciesSchema, handleSearchVacancies, getVacancySchema, handleGetVacancy } from "./tools/vacancies.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { searchVacanciesSchema, handleSearchVacancies, getVacancySchema, handleGetVacancy, getSimilarVacanciesSchema, handleGetSimilarVacancies, } from "./tools/vacancies.js";
|
|
6
|
+
import { searchEmployersSchema, handleSearchEmployers, getEmployerSchema, handleGetEmployer, getEmployerVacanciesSchema, handleGetEmployerVacancies, } from "./tools/employers.js";
|
|
7
|
+
import { searchResumesSchema, handleSearchResumes, getResumeSchema, handleGetResume, } from "./tools/resumes.js";
|
|
8
|
+
import { handleGetAreas, handleGetProfessionalRoles, handleGetDictionaries, suggestPositionsSchema, handleSuggestPositions, suggestCompaniesSchema, handleSuggestCompanies, suggestAreasSchema, handleSuggestAreas, } from "./tools/references.js";
|
|
9
|
+
import { getSalaryStatisticsSchema, handleGetSalaryStatistics, } from "./tools/salary.js";
|
|
10
|
+
import { HhApiError } from "./client.js";
|
|
9
11
|
import http from "node:http";
|
|
10
|
-
|
|
12
|
+
const TOOL_COUNT = 16;
|
|
13
|
+
function wrapHandler(fn) {
|
|
14
|
+
return async (params) => {
|
|
15
|
+
try {
|
|
16
|
+
const text = await fn(params);
|
|
17
|
+
return { content: [{ type: "text", text }] };
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
const message = error instanceof HhApiError
|
|
21
|
+
? `Error ${error.status}: ${error.message}${error.body ? `\n${error.body}` : ""}`
|
|
22
|
+
: error instanceof Error
|
|
23
|
+
? error.message
|
|
24
|
+
: String(error);
|
|
25
|
+
return { content: [{ type: "text", text: `ERROR: ${message}` }] };
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function createServer() {
|
|
11
30
|
const server = new McpServer({
|
|
12
31
|
name: "hh-mcp",
|
|
13
|
-
version: "
|
|
32
|
+
version: "2.0.0",
|
|
14
33
|
});
|
|
15
|
-
|
|
16
|
-
server.tool("
|
|
17
|
-
server.tool("
|
|
18
|
-
server.tool("
|
|
19
|
-
|
|
20
|
-
server.tool("
|
|
21
|
-
server.tool("
|
|
22
|
-
|
|
34
|
+
// --- Vacancies (3) ---
|
|
35
|
+
server.tool("search_vacancies", "Search job vacancies on hh.ru by keywords, region, salary, experience, employment type, schedule. Returns paginated list with title, salary, employer, snippet.", searchVacanciesSchema.shape, wrapHandler(handleSearchVacancies));
|
|
36
|
+
server.tool("get_vacancy", "Get full vacancy details: description, requirements, key skills, contacts, employer info.", getVacancySchema.shape, wrapHandler(handleGetVacancy));
|
|
37
|
+
server.tool("get_similar_vacancies", "Find vacancies similar to a given one. Useful for expanding a candidate's job search.", getSimilarVacanciesSchema.shape, wrapHandler(handleGetSimilarVacancies));
|
|
38
|
+
// --- Resumes (2) — require employer token ---
|
|
39
|
+
server.tool("search_resumes", "Search candidate resumes by keywords, region, professional role, salary, experience. Requires employer OAuth token (HH_ACCESS_TOKEN).", searchResumesSchema.shape, wrapHandler(handleSearchResumes));
|
|
40
|
+
server.tool("get_resume", "Get full resume details: experience, education, skills, contacts. Requires employer OAuth token (HH_ACCESS_TOKEN).", getResumeSchema.shape, wrapHandler(handleGetResume));
|
|
41
|
+
// --- Employers (3) ---
|
|
42
|
+
server.tool("search_employers", "Search companies/employers on hh.ru by name. Returns company info and open vacancy count.", searchEmployersSchema.shape, wrapHandler(handleSearchEmployers));
|
|
43
|
+
server.tool("get_employer", "Get detailed employer profile: description, industries, website, vacancy count.", getEmployerSchema.shape, wrapHandler(handleGetEmployer));
|
|
44
|
+
server.tool("get_employer_vacancies", "List active vacancies for a specific employer.", getEmployerVacanciesSchema.shape, wrapHandler(handleGetEmployerVacancies));
|
|
45
|
+
// --- Dictionaries & Suggests (6) ---
|
|
46
|
+
server.tool("get_areas", "Get the full tree of regions and cities with their codes. Use to find area IDs for search filters.", {}, wrapHandler(handleGetAreas));
|
|
47
|
+
server.tool("get_professional_roles", "Get the full tree of professional roles with IDs. Use to find role IDs for resume search and salary stats.", {}, wrapHandler(handleGetProfessionalRoles));
|
|
48
|
+
server.tool("get_dictionaries", "Get all reference dictionaries: currencies, employment types, schedules, experience levels, vacancy types, and more.", {}, wrapHandler(handleGetDictionaries));
|
|
49
|
+
server.tool("suggest_positions", "Autocomplete job titles / professional roles. Returns matching role suggestions for partial input.", suggestPositionsSchema.shape, wrapHandler(handleSuggestPositions));
|
|
50
|
+
server.tool("suggest_companies", "Autocomplete company names. Returns matching employer suggestions for partial input.", suggestCompaniesSchema.shape, wrapHandler(handleSuggestCompanies));
|
|
51
|
+
server.tool("suggest_areas", "Autocomplete region/city names. Returns matching area suggestions for partial input.", suggestAreasSchema.shape, wrapHandler(handleSuggestAreas));
|
|
52
|
+
// --- Salary (1) ---
|
|
53
|
+
server.tool("get_salary_statistics", "Get salary statistics for a professional role in a region. Returns vacancy count and salary distribution data.", getSalaryStatisticsSchema.shape, wrapHandler(handleGetSalaryStatistics));
|
|
23
54
|
return server;
|
|
24
55
|
}
|
|
56
|
+
// --- Start ---
|
|
25
57
|
async function main() {
|
|
26
58
|
const args = process.argv.slice(2);
|
|
27
59
|
const httpMode = args.includes("--http") || !!process.env.HTTP_PORT;
|
|
28
60
|
const port = Number(process.env.HTTP_PORT || process.env.PORT || 3000);
|
|
29
61
|
if (httpMode) {
|
|
30
62
|
const server = createServer();
|
|
31
|
-
const transport = new StreamableHTTPServerTransport({
|
|
63
|
+
const transport = new StreamableHTTPServerTransport({
|
|
64
|
+
sessionIdGenerator: () => crypto.randomUUID(),
|
|
65
|
+
});
|
|
32
66
|
await server.connect(transport);
|
|
33
67
|
const httpServer = http.createServer(async (req, res) => {
|
|
34
68
|
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
35
69
|
if (url.pathname === "/health") {
|
|
36
70
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
37
|
-
res.end(JSON.stringify({ status: "ok", tools:
|
|
71
|
+
res.end(JSON.stringify({ status: "ok", tools: TOOL_COUNT }));
|
|
38
72
|
return;
|
|
39
73
|
}
|
|
40
74
|
if (url.pathname === "/mcp") {
|
|
@@ -52,12 +86,11 @@ async function main() {
|
|
|
52
86
|
const server = createServer();
|
|
53
87
|
const transport = new StdioServerTransport();
|
|
54
88
|
await server.connect(transport);
|
|
55
|
-
console.error(
|
|
89
|
+
console.error(`[hh-mcp] Server started (stdio). ${TOOL_COUNT} tools. Auth: ${process.env.HH_ACCESS_TOKEN ? "token set" : "no token (public endpoints only)"}`);
|
|
56
90
|
}
|
|
57
91
|
}
|
|
58
|
-
export { createServer };
|
|
59
92
|
main().catch((error) => {
|
|
60
|
-
console.error("[hh-mcp]
|
|
93
|
+
console.error("[hh-mcp] Fatal error:", error);
|
|
61
94
|
process.exit(1);
|
|
62
95
|
});
|
|
63
96
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB,SAAS,WAAW,CAClB,EAAoC;IAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YAC9B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GACX,KAAK,YAAY,UAAU;gBACzB,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;gBACjF,CAAC,CAAC,KAAK,YAAY,KAAK;oBACtB,CAAC,CAAC,KAAK,CAAC,OAAO;oBACf,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;QAC7E,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,wBAAwB;IAExB,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,iKAAiK,EACjK,qBAAqB,CAAC,KAAK,EAC3B,WAAW,CAAC,qBAAqB,CAAC,CACnC,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,aAAa,EACb,2FAA2F,EAC3F,gBAAgB,CAAC,KAAK,EACtB,WAAW,CAAC,gBAAgB,CAAC,CAC9B,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,uFAAuF,EACvF,yBAAyB,CAAC,KAAK,EAC/B,WAAW,CAAC,yBAAyB,CAAC,CACvC,CAAC;IAEF,+CAA+C;IAE/C,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,uIAAuI,EACvI,mBAAmB,CAAC,KAAK,EACzB,WAAW,CAAC,mBAAmB,CAAC,CACjC,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,YAAY,EACZ,oHAAoH,EACpH,eAAe,CAAC,KAAK,EACrB,WAAW,CAAC,eAAe,CAAC,CAC7B,CAAC;IAEF,wBAAwB;IAExB,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,2FAA2F,EAC3F,qBAAqB,CAAC,KAAK,EAC3B,WAAW,CAAC,qBAAqB,CAAC,CACnC,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,cAAc,EACd,iFAAiF,EACjF,iBAAiB,CAAC,KAAK,EACvB,WAAW,CAAC,iBAAiB,CAAC,CAC/B,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,gDAAgD,EAChD,0BAA0B,CAAC,KAAK,EAChC,WAAW,CAAC,0BAA0B,CAAC,CACxC,CAAC;IAEF,sCAAsC;IAEtC,MAAM,CAAC,IAAI,CACT,WAAW,EACX,oGAAoG,EACpG,EAAE,EACF,WAAW,CAAC,cAAc,CAAC,CAC5B,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,4GAA4G,EAC5G,EAAE,EACF,WAAW,CAAC,0BAA0B,CAAC,CACxC,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,sHAAsH,EACtH,EAAE,EACF,WAAW,CAAC,qBAAqB,CAAC,CACnC,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,oGAAoG,EACpG,sBAAsB,CAAC,KAAK,EAC5B,WAAW,CAAC,sBAAsB,CAAC,CACpC,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,sFAAsF,EACtF,sBAAsB,CAAC,KAAK,EAC5B,WAAW,CAAC,sBAAsB,CAAC,CACpC,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,eAAe,EACf,sFAAsF,EACtF,kBAAkB,CAAC,KAAK,EACxB,WAAW,CAAC,kBAAkB,CAAC,CAChC,CAAC;IAEF,qBAAqB;IAErB,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,gHAAgH,EAChH,yBAAyB,CAAC,KAAK,EAC/B,WAAW,CAAC,yBAAyB,CAAC,CACvC,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gBAAgB;AAEhB,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IACpE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IAEvE,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;YAClD,kBAAkB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE;SAC9C,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEhC,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YACtD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC;YAEhE,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC/B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;gBAC7D,OAAO;YACT,CAAC;YAED,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBACxC,OAAO;YACT,CAAC;YAED,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACnB,GAAG,CAAC,GAAG,CACL,mEAAmE,CACpE,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YAC3B,OAAO,CAAC,KAAK,CAAC,8BAA8B,IAAI,kBAAkB,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,CACX,oCAAoC,UAAU,iBAAiB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,kCAAkC,EAAE,CAChJ,CAAC;IACJ,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|