@theyahia/hh-mcp 1.0.1 → 2.0.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/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,70 +1,167 @@
1
- # @theyahia/hh-mcp
2
-
3
- MCP-сервер для hh.ru API — поиск вакансий, зарплаты, работодатели, справочники. **6 инструментов. Без авторизации.**
4
-
5
- [![npm](https://img.shields.io/npm/v/@theyahia/hh-mcp)](https://www.npmjs.com/package/@theyahia/hh-mcp)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
-
8
- Часть серии [Russian API MCP](https://github.com/theYahia/russian-mcp) (50 серверов) by [@theYahia](https://github.com/theYahia).
9
-
10
- ## Установка
11
-
12
- ### Claude Desktop
13
-
14
- ```json
15
- {
16
- "mcpServers": {
17
- "hh": {
18
- "command": "npx",
19
- "args": ["-y", "@theyahia/hh-mcp"]
20
- }
21
- }
22
- }
23
- ```
24
-
25
- ### Claude Code
26
-
27
- ```bash
28
- claude mcp add hh -- npx -y @theyahia/hh-mcp
29
- ```
30
-
31
- ### VS Code / Cursor
32
-
33
- ```json
34
- { "servers": { "hh": { "command": "npx", "args": ["-y", "@theyahia/hh-mcp"] } } }
35
- ```
36
-
37
- ### Windsurf
38
-
39
- ```json
40
- { "mcpServers": { "hh": { "command": "npx", "args": ["-y", "@theyahia/hh-mcp"] } } }
41
- ```
42
-
43
- > Авторизация **не нужна** для поиска вакансий. Для доступа к резюме — задайте `HH_ACCESS_TOKEN`.
44
-
45
- ## Инструменты (6)
46
-
47
- | Инструмент | Описание |
48
- |------------|----------|
49
- | `search_vacancies` | Поиск вакансий по словам, региону, зарплате, опыту |
50
- | `get_vacancy` | Полная информация о вакансии с описанием и контактами |
51
- | `get_employers` | Поиск работодателей по названию |
52
- | `get_salary_stats` | Статистика зарплат по специальности и региону |
53
- | `get_areas` | Справочник регионов РФ и СНГ |
54
- | `get_professional_roles` | Справочник профессий |
55
-
56
- ## Примеры
57
-
58
- ```
59
- Найди вакансии Python разработчика в Москве от 200000 рублей
60
- Покажи вакансии в Яндексе
61
- Какая средняя зарплата Senior Backend в Петербурге?
62
- ```
63
-
64
- ## Часть серии Russian API MCP
65
-
66
- **50 серверов:** [github.com/theYahia/russian-mcp](https://github.com/theYahia/russian-mcp)
67
-
68
- ## Лицензия
69
-
70
- MIT
1
+ # @theyahia/hh-mcp
2
+
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
+
5
+ [![npm](https://img.shields.io/npm/v/@theyahia/hh-mcp)](https://www.npmjs.com/package/@theyahia/hh-mcp)
6
+ [![CI](https://github.com/theYahia/hh-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/theYahia/hh-mcp/actions)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ Part of the [Russian API MCP](https://github.com/theYahia/russian-mcp) series by [@theYahia](https://github.com/theYahia).
10
+
11
+ ## Two Modes
12
+
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
+
18
+ Get a token at [dev.hh.ru/admin](https://dev.hh.ru/admin).
19
+
20
+ ## Installation
21
+
22
+ ### Claude Desktop
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "hh": {
28
+ "command": "npx",
29
+ "args": ["-y", "@theyahia/hh-mcp"],
30
+ "env": {
31
+ "HH_ACCESS_TOKEN": "optional-oauth-token"
32
+ }
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ### Claude Code
39
+
40
+ ```bash
41
+ claude mcp add hh -- npx -y @theyahia/hh-mcp
42
+ # With token:
43
+ claude mcp add hh -e HH_ACCESS_TOKEN=your-token -- npx -y @theyahia/hh-mcp
44
+ ```
45
+
46
+ ### VS Code / Cursor
47
+
48
+ ```json
49
+ {
50
+ "servers": {
51
+ "hh": {
52
+ "command": "npx",
53
+ "args": ["-y", "@theyahia/hh-mcp"]
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### Windsurf
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "hh": {
65
+ "command": "npx",
66
+ "args": ["-y", "@theyahia/hh-mcp"]
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### HTTP Mode (Streamable HTTP)
73
+
74
+ ```bash
75
+ npx @theyahia/hh-mcp --http
76
+ # or
77
+ HTTP_PORT=8080 npx @theyahia/hh-mcp --http
78
+ ```
79
+
80
+ Endpoint: `http://localhost:3000/mcp`
81
+ Health check: `http://localhost:3000/health`
82
+
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
93
+
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 |
99
+
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
137
+
138
+ ```
139
+ Find remote Python developer jobs in Moscow paying over 300,000 RUB
140
+ ```
141
+
142
+ ```
143
+ Show me all open vacancies at Yandex and give me salary statistics for their top roles
144
+ ```
145
+
146
+ ```
147
+ Compare Senior Backend salaries in Moscow vs Saint Petersburg, and suggest similar vacancies to the best-paying one
148
+ ```
149
+
150
+ ## Development
151
+
152
+ ```bash
153
+ git clone https://github.com/theYahia/hh-mcp.git
154
+ cd hh-mcp
155
+ npm install
156
+ npm run build
157
+ npm test
158
+ ```
159
+
160
+ ## API Reference
161
+
162
+ - [hh.ru API docs](https://api.hh.ru/)
163
+ - [hh.ru API GitHub](https://github.com/hhru/api)
164
+
165
+ ## License
166
+
167
+ MIT
package/dist/client.d.ts CHANGED
@@ -1 +1,7 @@
1
+ export declare class HhApiError extends Error {
2
+ status: number;
3
+ statusText: string;
4
+ body?: string | undefined;
5
+ constructor(status: number, statusText: string, body?: string | undefined);
6
+ }
1
7
  export declare function hhGet(path: string): Promise<unknown>;
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/1.0 (https://github.com/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
- "Accept": "application/json",
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
- if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
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}, повтор через ${delay}мс (${attempt}/${MAX_RETRIES})`);
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 Error(`hh.ru HTTP ${response.status}: ${response.statusText}`);
84
+ throw new HhApiError(response.status, response.statusText, body);
32
85
  }
33
86
  catch (error) {
34
87
  clearTimeout(timer);
35
- if (error instanceof DOMException && error.name === "AbortError" && attempt < MAX_RETRIES) {
36
- console.error(`[hh-mcp] Таймаут, повтор (${attempt}/${MAX_RETRIES})`);
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
@@ -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,QAAQ,EAAE,kBAAkB;SAC7B,CAAC;QAEF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QAC1C,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;QAC/C,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,EAAE,EAAE;gBACjD,OAAO;gBACP,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YACH,YAAY,CAAC,KAAK,CAAC,CAAC;YAEpB,IAAI,QAAQ,CAAC,EAAE;gBAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;YAExC,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBACjF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;gBACxD,OAAO,CAAC,KAAK,CAAC,YAAY,QAAQ,CAAC,MAAM,kBAAkB,KAAK,OAAO,OAAO,IAAI,WAAW,GAAG,CAAC,CAAC;gBAClG,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;gBAC7C,SAAS;YACX,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,cAAc,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QAC3E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC1F,OAAO,CAAC,KAAK,CAAC,6BAA6B,OAAO,IAAI,WAAW,GAAG,CAAC,CAAC;gBACtE,SAAS;YACX,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAClD,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function createServer(): McpServer;
package/dist/index.js CHANGED
@@ -1,26 +1,96 @@
1
1
  #!/usr/bin/env node
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { searchVacanciesSchema, handleSearchVacancies, getVacancySchema, handleGetVacancy } from "./tools/vacancies.js";
5
- import { getEmployersSchema, handleGetEmployers } from "./tools/employers.js";
6
- import { handleGetAreas, handleGetProfessionalRoles, getSalaryStatsSchema, handleGetSalaryStats } from "./tools/references.js";
7
- const server = new McpServer({
8
- name: "hh-mcp",
9
- version: "1.0.0",
10
- });
11
- server.tool("search_vacancies", "Поиск вакансий на hh.ru по ключевым словам, региону, зарплате, опыту.", searchVacanciesSchema.shape, async (params) => ({ content: [{ type: "text", text: await handleSearchVacancies(params) }] }));
12
- server.tool("get_vacancy", "Полная информация о вакансии: описание, требования, навыки, контакты.", getVacancySchema.shape, async (params) => ({ content: [{ type: "text", text: await handleGetVacancy(params) }] }));
13
- server.tool("get_employers", "Поиск работодателей по названию.", getEmployersSchema.shape, async (params) => ({ content: [{ type: "text", text: await handleGetEmployers(params) }] }));
14
- server.tool("get_salary_stats", "Статистика зарплат по специальности и региону.", getSalaryStatsSchema.shape, async (params) => ({ content: [{ type: "text", text: await handleGetSalaryStats(params) }] }));
15
- server.tool("get_areas", "Справочник регионов РФ и СНГ с кодами для поиска.", {}, async () => ({ content: [{ type: "text", text: await handleGetAreas() }] }));
16
- server.tool("get_professional_roles", "Справочник профессиональных ролей для поиска вакансий.", {}, async () => ({ content: [{ type: "text", text: await handleGetProfessionalRoles() }] }));
4
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
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";
11
+ import http from "node:http";
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() {
30
+ const server = new McpServer({
31
+ name: "hh-mcp",
32
+ version: "2.0.0",
33
+ });
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));
54
+ return server;
55
+ }
56
+ // --- Start ---
17
57
  async function main() {
18
- const transport = new StdioServerTransport();
19
- await server.connect(transport);
20
- console.error("[hh-mcp] Сервер запущен. 6 инструментов. Авторизация не требуется для поиска.");
58
+ const args = process.argv.slice(2);
59
+ const httpMode = args.includes("--http") || !!process.env.HTTP_PORT;
60
+ const port = Number(process.env.HTTP_PORT || process.env.PORT || 3000);
61
+ if (httpMode) {
62
+ const server = createServer();
63
+ const transport = new StreamableHTTPServerTransport({
64
+ sessionIdGenerator: () => crypto.randomUUID(),
65
+ });
66
+ await server.connect(transport);
67
+ const httpServer = http.createServer(async (req, res) => {
68
+ const url = new URL(req.url || "/", `http://localhost:${port}`);
69
+ if (url.pathname === "/health") {
70
+ res.writeHead(200, { "Content-Type": "application/json" });
71
+ res.end(JSON.stringify({ status: "ok", tools: TOOL_COUNT }));
72
+ return;
73
+ }
74
+ if (url.pathname === "/mcp") {
75
+ await transport.handleRequest(req, res);
76
+ return;
77
+ }
78
+ res.writeHead(404);
79
+ res.end("Not found. Use /mcp for MCP protocol or /health for health check.");
80
+ });
81
+ httpServer.listen(port, () => {
82
+ console.error(`[hh-mcp] HTTP mode on port ${port}. Endpoint: /mcp`);
83
+ });
84
+ }
85
+ else {
86
+ const server = createServer();
87
+ const transport = new StdioServerTransport();
88
+ await server.connect(transport);
89
+ console.error(`[hh-mcp] Server started (stdio). ${TOOL_COUNT} tools. Auth: ${process.env.HH_ACCESS_TOKEN ? "token set" : "no token (public endpoints only)"}`);
90
+ }
21
91
  }
22
92
  main().catch((error) => {
23
- console.error("[hh-mcp] Ошибка:", error);
93
+ console.error("[hh-mcp] Fatal error:", error);
24
94
  process.exit(1);
25
95
  });
26
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,qBAAqB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxH,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE/H,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,uEAAuE,EACvE,qBAAqB,CAAC,KAAK,EAC3B,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAC/F,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,aAAa,EACb,uEAAuE,EACvE,gBAAgB,CAAC,KAAK,EACtB,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAC1F,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,eAAe,EACf,kCAAkC,EAClC,kBAAkB,CAAC,KAAK,EACxB,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAC5F,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,gDAAgD,EAChD,oBAAoB,CAAC,KAAK,EAC1B,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAC9F,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,WAAW,EACX,mDAAmD,EACnD,EAAE,EACF,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,cAAc,EAAE,EAAE,CAAC,EAAE,CAAC,CAC5E,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,wDAAwD,EACxD,EAAE,EACF,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,0BAA0B,EAAE,EAAE,CAAC,EAAE,CAAC,CACxF,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,+EAA+E,CAAC,CAAC;AACjG,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};