@sargonpiraev/hh-mcp-server 1.14.0 → 1.16.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 +280 -33
- package/build/main.d.ts +7 -0
- package/build/main.js +421 -0
- package/build/server.d.ts +27 -0
- package/build/server.js +3062 -0
- package/package.json +28 -19
- package/build/index.d.ts +0 -2
- package/build/index.js +0 -499
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sargonpiraev/hh-mcp-server",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MCP Server
|
|
3
|
+
"version": "1.16.0",
|
|
4
|
+
"description": "HeadHunter API MCP Server provides seamless integration between AI tools and HeadHunter's comprehensive job search platform.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
7
7
|
"claude",
|
|
8
8
|
"ai",
|
|
9
|
-
"assistant"
|
|
9
|
+
"assistant",
|
|
10
|
+
"hh"
|
|
10
11
|
],
|
|
11
|
-
"main": "./build/
|
|
12
|
-
"types": "./build/
|
|
12
|
+
"main": "./build/main.js",
|
|
13
|
+
"types": "./build/main.d.ts",
|
|
13
14
|
"type": "module",
|
|
14
15
|
"files": [
|
|
15
16
|
"build/",
|
|
@@ -17,11 +18,11 @@
|
|
|
17
18
|
"LICENSE"
|
|
18
19
|
],
|
|
19
20
|
"bin": {
|
|
20
|
-
"hh-mcp-server": "./build/
|
|
21
|
+
"hh-mcp-server": "./build/main.js"
|
|
21
22
|
},
|
|
22
23
|
"scripts": {
|
|
23
24
|
"test": "jest --passWithNoTests --coverage",
|
|
24
|
-
"dev": "tsx watch ./src/
|
|
25
|
+
"dev": "tsx watch ./src/main.ts",
|
|
25
26
|
"build": "tsc -p tsconfig.json",
|
|
26
27
|
"lint": "eslint ./src --ext .ts",
|
|
27
28
|
"format": "prettier --write .",
|
|
@@ -30,11 +31,19 @@
|
|
|
30
31
|
"commitlint": "commitlint --edit",
|
|
31
32
|
"typecheck": "tsc --noEmit",
|
|
32
33
|
"audit": "npm audit --omit=dev",
|
|
33
|
-
"inspect": "npx @modelcontextprotocol/inspector
|
|
34
|
+
"inspect": "npx @modelcontextprotocol/inspector --config mcp.json --server hh"
|
|
34
35
|
},
|
|
35
36
|
"engines": {
|
|
36
37
|
"node": ">=18.0.0"
|
|
37
38
|
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@modelcontextprotocol/sdk": "^1.11.0",
|
|
41
|
+
"dotenv": "^16.5.0",
|
|
42
|
+
"zod": "^3.24.3",
|
|
43
|
+
"axios": "^1.7.7",
|
|
44
|
+
"express": "^4.18.2",
|
|
45
|
+
"cors": "^2.8.5"
|
|
46
|
+
},
|
|
38
47
|
"devDependencies": {
|
|
39
48
|
"@commitlint/cli": "^19.8.0",
|
|
40
49
|
"@commitlint/config-conventional": "^19.8.0",
|
|
@@ -45,6 +54,8 @@
|
|
|
45
54
|
"@semantic-release/release-notes-generator": "^14.0.3",
|
|
46
55
|
"@types/debug": "^4.1.12",
|
|
47
56
|
"@types/node": "^22.15.3",
|
|
57
|
+
"@types/express": "^4.17.17",
|
|
58
|
+
"@types/cors": "^2.8.13",
|
|
48
59
|
"eslint": "^9.26.0",
|
|
49
60
|
"globals": "^16.0.0",
|
|
50
61
|
"husky": "^9.1.7",
|
|
@@ -59,19 +70,17 @@
|
|
|
59
70
|
"typescript-eslint": "^8.31.1",
|
|
60
71
|
"@types/minimatch": "^5.1.2"
|
|
61
72
|
},
|
|
62
|
-
"dependencies": {
|
|
63
|
-
"@modelcontextprotocol/sdk": "^1.11.0",
|
|
64
|
-
"dotenv": "^16.5.0",
|
|
65
|
-
"zod": "^3.24.3",
|
|
66
|
-
"axios": "^1.7.7"
|
|
67
|
-
},
|
|
68
73
|
"publishConfig": {
|
|
69
74
|
"access": "public"
|
|
70
75
|
},
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
76
|
+
"repository": {
|
|
77
|
+
"type": "git",
|
|
78
|
+
"url": ""
|
|
79
|
+
},
|
|
80
|
+
"bugs": {
|
|
81
|
+
"url": ""
|
|
75
82
|
},
|
|
76
|
-
"
|
|
83
|
+
"homepage": "",
|
|
84
|
+
"author": "",
|
|
85
|
+
"license": ""
|
|
77
86
|
}
|
package/build/index.d.ts
DELETED
package/build/index.js
DELETED
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
import axios from 'axios';
|
|
6
|
-
import dotenv from 'dotenv';
|
|
7
|
-
dotenv.config();
|
|
8
|
-
const envSchema = z.object({
|
|
9
|
-
HH_API_TOKEN: z.string(),
|
|
10
|
-
});
|
|
11
|
-
const env = envSchema.parse(process.env);
|
|
12
|
-
const mcpServer = new McpServer({
|
|
13
|
-
name: '@sargonpiraev/hh-mcp-server',
|
|
14
|
-
version: '',
|
|
15
|
-
}, {
|
|
16
|
-
instructions: `In English | [По-русски](https://api.hh.ru/openapi/redoc)
|
|
17
|
-
|
|
18
|
-
Documentation [in GitHub](https://github.com/hhru/api/blob/master/docs_eng/README.md).
|
|
19
|
-
|
|
20
|
-
You can use Ctrl+F to search through the documentation.
|
|
21
|
-
|
|
22
|
-
# General information
|
|
23
|
-
|
|
24
|
-
* The whole API works using the HTTPS protocol.
|
|
25
|
-
* Authorization is performed via the OAuth2 protocol.
|
|
26
|
-
* All data is available only in the JSON format.
|
|
27
|
-
* The basic URL is `https://api.hh.ru/`
|
|
28
|
-
* Requests for [any site of HeadHunter Groups of Companies](#section/General-information/Host-selection)
|
|
29
|
-
are available
|
|
30
|
-
* <a name="date-format"></a> Dates are formatted according to
|
|
31
|
-
[ISO 8601](http://en.wikipedia.org/wiki/ISO_8601): `YYYY-MM-DDThh:mm:ss±hhmm`.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
## Request requirements
|
|
35
|
-
|
|
36
|
-
Your request should send the `User-Agent` header, but if your
|
|
37
|
-
HTTP client does not allow it, you can send an `HH-User-Agent` header. If no header is sent,
|
|
38
|
-
you will receive the `400 Bad Request` as a response.
|
|
39
|
-
By specifying the name of the application and the developer's contact email in the header,
|
|
40
|
-
you will help us to contact you promptly if required.
|
|
41
|
-
The `User-Agent` and `HH-User-Agent` headers are interchangeable. If you send both headers,
|
|
42
|
-
only `HH-User-Agent` is processed.
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
User-Agent: MyApp/1.0 (my-app-feedback@example.com)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
More detail on [errors in the User-Agent title](https://github.com/hhru/api/blob/master/docs_eng/errors.md#user-agent).
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
### Request body format when sending JSON
|
|
52
|
-
|
|
53
|
-
Data transferred in the request body must meet the requirements:
|
|
54
|
-
|
|
55
|
-
* Valid JSON (it is acceptable to transfer both minified and
|
|
56
|
-
pretty-printed variants with extra whitespace and line breaks).
|
|
57
|
-
|
|
58
|
-
* It is recommended to use the UTF-8 encoding without extra escaping
|
|
59
|
-
(`{"name": "Иванов Иван"}`).
|
|
60
|
-
|
|
61
|
-
* It is also possible to use the ascii encoding with escaping
|
|
62
|
-
(`{"name": "\u0418\u0432\u0430\u043d\u043e\u0432 \u0418\u0432\u0430\u043d"}`).
|
|
63
|
-
|
|
64
|
-
* Data types in certain fields are supplemented with additional conditions
|
|
65
|
-
described in each specific method. In JSON, data types are `string`, `number`,
|
|
66
|
-
`boolean`, `null`, `object`, `array`.
|
|
67
|
-
|
|
68
|
-
### Response
|
|
69
|
-
A response that exceeds a certain length will be compressed using gzip.
|
|
70
|
-
|
|
71
|
-
### Errors and response codes
|
|
72
|
-
|
|
73
|
-
API extensively uses informing through response codes.
|
|
74
|
-
The application must process them correctly.
|
|
75
|
-
|
|
76
|
-
In case of failure or breakdown, responses with the `503` and `500` code may be
|
|
77
|
-
returned.
|
|
78
|
-
|
|
79
|
-
When an error occurs, the response body, besides the response code,
|
|
80
|
-
may have additional information allowing the developer
|
|
81
|
-
to learn the cause of a particular response.
|
|
82
|
-
|
|
83
|
-
[More detail on possible errors](https://github.com/hhru/api/blob/master/docs_eng/errors.md).
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
## Undocumented fields and request parameters
|
|
87
|
-
|
|
88
|
-
In responses and API parameters you can find keys that are not described in the
|
|
89
|
-
documentation. It usually means that they are left for compatibility with older
|
|
90
|
-
versions. It is not recommended to use them. If your application already uses
|
|
91
|
-
such keys, switch to using desirable keys described in the documentation.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
## Pagination
|
|
95
|
-
|
|
96
|
-
For every request that presupposes the return of object list you can indicate
|
|
97
|
-
`page=N&per_page=M` in the parameters. Numeration starts from zero; the first
|
|
98
|
-
(zero) page is returned by default with 20 objects on it. In all responses where
|
|
99
|
-
pagination is available, the uniform root object:
|
|
100
|
-
|
|
101
|
-
```json
|
|
102
|
-
{
|
|
103
|
-
"found": 1,
|
|
104
|
-
"per_page": 1,
|
|
105
|
-
"pages": 1,
|
|
106
|
-
"page": 0,
|
|
107
|
-
"items": [{}]
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Host selection
|
|
112
|
-
|
|
113
|
-
HeadHunter API allows you to obtain data from all of the HeadHunter group of
|
|
114
|
-
companies websites.
|
|
115
|
-
|
|
116
|
-
In particular:
|
|
117
|
-
|
|
118
|
-
* hh.ru
|
|
119
|
-
* rabota.by
|
|
120
|
-
* hh1.az
|
|
121
|
-
* hh.uz
|
|
122
|
-
* hh.kz
|
|
123
|
-
* headhunter.ge
|
|
124
|
-
* headhunter.kg
|
|
125
|
-
|
|
126
|
-
Requests for data on all the websites should be made to `https://api.hh.ru/`.
|
|
127
|
-
|
|
128
|
-
If you need to track the specific websites, you can add `?host=` parameter to
|
|
129
|
-
your request. Default is `hh.ru`.
|
|
130
|
-
|
|
131
|
-
Example: to obtain [localizations](https://api.hh.ru/openapi/en/redoc#tag/Public-directories/operation/get-locales) available on hh.kz, you should
|
|
132
|
-
make a GET-request to `https://api.hh.ru/locales?host=hh.kz`.
|
|
133
|
-
|
|
134
|
-
## CORS (Cross-Origin Resource Sharing)
|
|
135
|
-
|
|
136
|
-
API supports the CORS technology for requesting browser data from any domain.
|
|
137
|
-
This method is preferable to JSONP. It is not restricted to the GET method. To
|
|
138
|
-
debug CORS, a [special method](https://github.com/hhru/api/blob/master/docs_eng/cors.md) is available. To use JSONP, transfer the
|
|
139
|
-
`?callback=callback_name` parameter.
|
|
140
|
-
|
|
141
|
-
* [CORS specification on w3.org](http://www.w3.org/TR/cors/)
|
|
142
|
-
* [HTML5Rocks CORS Tutorial](http://www.html5rocks.com/en/tutorials/cors/)
|
|
143
|
-
* [CORS on dev.opera.com](http://dev.opera.com/articles/view/dom-access-control-using-cross-origin-resource-sharing/)
|
|
144
|
-
* [CORS on caniuse.com](http://caniuse.com/#feat=cors)
|
|
145
|
-
* [CORS on en.wikipedia.org](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
## External links to articles and standards
|
|
149
|
-
|
|
150
|
-
* [HTTP/1.1](http://tools.ietf.org/html/rfc2616)
|
|
151
|
-
* [JSON](http://json.org/)
|
|
152
|
-
* [URI Template](http://tools.ietf.org/html/rfc6570)
|
|
153
|
-
* [OAuth 2.0](http://tools.ietf.org/html/rfc6749)
|
|
154
|
-
* [REST](http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)
|
|
155
|
-
* [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601)
|
|
156
|
-
|
|
157
|
-
# Authorization
|
|
158
|
-
|
|
159
|
-
The API supports the following authorization levels:
|
|
160
|
-
- [application authorization](#tag/Application-authorization)
|
|
161
|
-
- [user authorization](#section/Authorization/User-authorization)
|
|
162
|
-
|
|
163
|
-
* [Obtaining user authorization](#section/Authorization/User-authorization)
|
|
164
|
-
* [Rules for generating special `redirect_uri`](#section/Authorization/Rules-for-generating-special-redirect_uri)
|
|
165
|
-
* [Authorization process](#section/Authorization/Authorization-process)
|
|
166
|
-
* [Successful obtaining temporary `authorization_code`](#get-authorization_code)
|
|
167
|
-
* [Obtaining access and refresh tokens](#section/Authorization/Obtaining-access-and-refresh-tokens)
|
|
168
|
-
* [Updating a pair of access and refresh tokens](#section/Authorization/Updating-a-pair-of-access-and-refresh-tokens)
|
|
169
|
-
* [Authorization request under another user](#section/Authorization/Authorization-request-under-another-user)
|
|
170
|
-
* [Authorization under different working account](#section/Authorization/Authorization-under-different-working-account)
|
|
171
|
-
* [Obtaining application authorization](#tag/Application-authorization)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
## User authorization
|
|
175
|
-
To make requests as a user you need a user token.
|
|
176
|
-
|
|
177
|
-
Assign a user to the application (open the page) via the request:
|
|
178
|
-
|
|
179
|
-
```
|
|
180
|
-
https://hh.ru/oauth/authorize?
|
|
181
|
-
response_type=code&
|
|
182
|
-
client_id={client_id}&
|
|
183
|
-
state={state}&
|
|
184
|
-
redirect_uri={redirect_uri}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
Required parameters:
|
|
188
|
-
|
|
189
|
-
* `response_type=code` — specifies authorization method using the `authorization code`
|
|
190
|
-
* `client_id` — ID obtained when creating the application
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Optional parameters:
|
|
194
|
-
|
|
195
|
-
* `state` — if specified, it will be added in the redirect link of the response.
|
|
196
|
-
|
|
197
|
-
It allows to prevent cross-site request forgery. More info see in [RFC 6749. Section 10.12](http://tools.ietf.org/html/rfc6749#section-10.12)
|
|
198
|
-
* `redirect_uri` — a URI for redirecting user after authorization.
|
|
199
|
-
|
|
200
|
-
If not specified, the value from the application settings is used. If the value is specified, its validation is carried out. Probably, you will need to make `urlencode` of the value.
|
|
201
|
-
|
|
202
|
-
## Rules for generating special redirect_uri
|
|
203
|
-
|
|
204
|
-
For example, if `http://example.com/oauth` is saved in the settings, it is allowed to specify:
|
|
205
|
-
|
|
206
|
-
* `http://www.example.com/oauth` — a subdomain;
|
|
207
|
-
* `http://www.example.com/oauth/sub/path` — a detailed path;
|
|
208
|
-
* `http://example.com/oauth?lang=RU` — an additional parameter;
|
|
209
|
-
* `http://www.example.com/oauth/sub/path?lang=RU` — all the mentioned.
|
|
210
|
-
|
|
211
|
-
Forbidden:
|
|
212
|
-
|
|
213
|
-
* `https://example.com/oauth` — another protocols;
|
|
214
|
-
* `http://wwwexample.com/oauth` — another domains;
|
|
215
|
-
* `http://wwwexample.com/` — another paths;
|
|
216
|
-
* `http://example.com/oauths` — another paths;
|
|
217
|
-
* `http://example.com:80/oauths` — originally missing port.
|
|
218
|
-
|
|
219
|
-
## Authorization process
|
|
220
|
-
|
|
221
|
-
If the user is not authorized, the site login form appears. When the authorization is done, the form with subject access request is shown.
|
|
222
|
-
|
|
223
|
-
If the user rejects the access request, it triggers the redirection on the specified `redirect_uri` with `?error=access_denied` and
|
|
224
|
-
`state={state}`, if any.
|
|
225
|
-
|
|
226
|
-
<a name="get-authorization_code"></a>
|
|
227
|
-
### Successful obtaining temporary `authorization_code`
|
|
228
|
-
|
|
229
|
-
If the user accepts the access request, the redirect link contains a temporary `authorization_code`:
|
|
230
|
-
|
|
231
|
-
```http
|
|
232
|
-
HTTP/1.1 302 FOUND
|
|
233
|
-
Location: {redirect_uri}?code={authorization_code}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
If the user is already authorized, and the access to the application was granted earlier, then the login and subject access request are omitted, and the above redirect link returns as a response.
|
|
237
|
-
|
|
238
|
-
## Obtaining access and refresh tokens
|
|
239
|
-
|
|
240
|
-
After obtaining the `authorization_code` the application makes a server-server request `POST https://api.hh.ru/token` to exchange the `authorization_code` to an `access_token` (old `POST https://hh.ru/oauth/token` request should consider as deprecated).
|
|
241
|
-
|
|
242
|
-
The request body should contain [additional parameters](#required_parameters).
|
|
243
|
-
|
|
244
|
-
The request body should be sent in standard `application/x-www-form-urlencoded` format with the corresponding `Content-Type` header.
|
|
245
|
-
|
|
246
|
-
The `authorization_code` has a short lifetime. When it expires, request another `authorization_code`.
|
|
247
|
-
|
|
248
|
-
## Updating a pair of access and refresh tokens
|
|
249
|
-
The `access_token` also has a lifetime (`expires_in` parameter, in seconds). When it expires, the application should make a request with the `refresh_token` to obtain a new one.
|
|
250
|
-
|
|
251
|
-
The request should be done in `application/x-www-form-urlencoded` format.
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
POST https://api.hh.ru/token
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
(old `POST https://hh.ru/oauth/token` request should consider as deprecated)
|
|
258
|
-
|
|
259
|
-
The request body should contain [additional parameters](#required_parameters).
|
|
260
|
-
|
|
261
|
-
You can use the `refresh_token` only once and only after expiration of the `access_token`.
|
|
262
|
-
|
|
263
|
-
A new pair of access and refresh tokens should be used for further API requests and requests for token prolongation.
|
|
264
|
-
|
|
265
|
-
## Authorization request under another user
|
|
266
|
-
|
|
267
|
-
The following case is possible:
|
|
268
|
-
|
|
269
|
-
1. Application redirects a user to the site with the authorization request.
|
|
270
|
-
2. The user is already authorized on site and application already has an access granted.
|
|
271
|
-
3. The user will be proposed to continue the work under current account or login to another one.
|
|
272
|
-
|
|
273
|
-
If you need to replace step 3 by instant redirection with temporary token, you should add the parameter `skip_choose_account=true` to `/oauth/authorize...` request. Then the authorized user gets access immediately.
|
|
274
|
-
|
|
275
|
-
If you need to show the authorization form every time, you should add the parameter `force_login=true` to `/oauth/authorize...` request. In this case the authorization form appears even if the user is already authorized.
|
|
276
|
-
|
|
277
|
-
This feature may be useful for applications that provide services for applicants only. If an employer user tries to log in, the application may propose them to get access under another user.
|
|
278
|
-
|
|
279
|
-
Also, after the authorization the application can show a message:
|
|
280
|
-
|
|
281
|
-
```
|
|
282
|
-
You logged in as %FirstName_LastName%. Is it not you?
|
|
283
|
-
```
|
|
284
|
-
and propose a link with `force_login=true` for authorization under another login.
|
|
285
|
-
|
|
286
|
-
## Authorization under different working account
|
|
287
|
-
|
|
288
|
-
To obtain information about getting a list of manager working accounts and working under different working accounts, see the [manager working account](#tag/Employer-managers/operation/get-manager-accounts) section.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
<!-- ReDoc-Inject: <security-definitions> -->
|
|
292
|
-
`,
|
|
293
|
-
capabilities: {
|
|
294
|
-
tools: {},
|
|
295
|
-
logging: {},
|
|
296
|
-
},
|
|
297
|
-
});
|
|
298
|
-
const logger = {
|
|
299
|
-
log: (...message) => mcpServer.server.sendLoggingMessage({ level: 'info', data: message.join(' ') }),
|
|
300
|
-
error: (...message) => mcpServer.server.sendLoggingMessage({ level: 'error', data: message.join(' ') }),
|
|
301
|
-
debug: (...message) => mcpServer.server.sendLoggingMessage({ level: 'debug', data: message.join(' ') }),
|
|
302
|
-
};
|
|
303
|
-
const apiClient = axios.create({
|
|
304
|
-
baseURL: 'https://api.hh.ru',
|
|
305
|
-
headers: {
|
|
306
|
-
Accept: 'application/json',
|
|
307
|
-
},
|
|
308
|
-
timeout: 30000,
|
|
309
|
-
});
|
|
310
|
-
apiClient.interceptors.request.use((config) => {
|
|
311
|
-
if (env.HH_API_TOKEN) {
|
|
312
|
-
config.headers['Authorization'] = env.HH_API_TOKEN;
|
|
313
|
-
}
|
|
314
|
-
return config;
|
|
315
|
-
}, (error) => {
|
|
316
|
-
return Promise.reject(error);
|
|
317
|
-
});
|
|
318
|
-
function handleResult(data) {
|
|
319
|
-
return {
|
|
320
|
-
content: [
|
|
321
|
-
{
|
|
322
|
-
type: 'text',
|
|
323
|
-
text: JSON.stringify(data, null, 2),
|
|
324
|
-
},
|
|
325
|
-
],
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
function handleError(error) {
|
|
329
|
-
console.error(error);
|
|
330
|
-
logger.error('Error occurred:', JSON.stringify(error));
|
|
331
|
-
if (axios.isAxiosError(error)) {
|
|
332
|
-
const message = error.response?.data?.message || error.message;
|
|
333
|
-
return {
|
|
334
|
-
isError: true,
|
|
335
|
-
content: [{ type: 'text', text: `API Error: ${message}` }],
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
return {
|
|
339
|
-
isError: true,
|
|
340
|
-
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
mcpServer.tool('confirm-phone-in-resume', `Verify phone with a code`, {}, async (args) => {
|
|
344
|
-
try {
|
|
345
|
-
const response = await apiClient.post('/resume_phone_confirm', args);
|
|
346
|
-
return handleResult(response.data);
|
|
347
|
-
}
|
|
348
|
-
catch (error) {
|
|
349
|
-
return handleError(error);
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
mcpServer.tool('get-manager-settings', `Manager preferences`, {
|
|
353
|
-
employer_id: z.string(),
|
|
354
|
-
manager_id: z.string(),
|
|
355
|
-
}, async (args) => {
|
|
356
|
-
try {
|
|
357
|
-
const { employer_id, manager_id, ...queryParams } = args;
|
|
358
|
-
const url = `/employers/${employer_id}/managers/${manager_id}/settings`;
|
|
359
|
-
const response = await apiClient.get(url, {
|
|
360
|
-
params: queryParams,
|
|
361
|
-
});
|
|
362
|
-
return handleResult(response.data);
|
|
363
|
-
}
|
|
364
|
-
catch (error) {
|
|
365
|
-
return handleError(error);
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
mcpServer.tool('get-employer-manager-limits', `Daily limit of resume views for current manager`, {
|
|
369
|
-
employer_id: z.string(),
|
|
370
|
-
manager_id: z.string(),
|
|
371
|
-
}, async (args) => {
|
|
372
|
-
try {
|
|
373
|
-
const { employer_id, manager_id, ...queryParams } = args;
|
|
374
|
-
const url = `/employers/${employer_id}/managers/${manager_id}/limits/resume`;
|
|
375
|
-
const response = await apiClient.get(url, {
|
|
376
|
-
params: queryParams,
|
|
377
|
-
});
|
|
378
|
-
return handleResult(response.data);
|
|
379
|
-
}
|
|
380
|
-
catch (error) {
|
|
381
|
-
return handleError(error);
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
mcpServer.tool('get-employer-addresses', `Directory of employer's addresses`, {
|
|
385
|
-
employer_id: z.string(),
|
|
386
|
-
changed_after: z.string().optional(),
|
|
387
|
-
manager_id: z.string().optional(),
|
|
388
|
-
with_manager: z.string().optional(),
|
|
389
|
-
per_page: z.string().optional(),
|
|
390
|
-
page: z.string().optional(),
|
|
391
|
-
}, async (args) => {
|
|
392
|
-
try {
|
|
393
|
-
const { employer_id, ...queryParams } = args;
|
|
394
|
-
const url = `/employers/${employer_id}/addresses`;
|
|
395
|
-
const response = await apiClient.get(url, {
|
|
396
|
-
params: queryParams,
|
|
397
|
-
});
|
|
398
|
-
return handleResult(response.data);
|
|
399
|
-
}
|
|
400
|
-
catch (error) {
|
|
401
|
-
return handleError(error);
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
mcpServer.tool('get-employer-managers', `Directory of employer's managers`, {
|
|
405
|
-
employer_id: z.string(),
|
|
406
|
-
page: z.string().optional(),
|
|
407
|
-
per_page: z.string().optional(),
|
|
408
|
-
search_text: z.string().optional(),
|
|
409
|
-
}, async (args) => {
|
|
410
|
-
try {
|
|
411
|
-
const { employer_id, ...queryParams } = args;
|
|
412
|
-
const url = `/employers/${employer_id}/managers`;
|
|
413
|
-
const response = await apiClient.get(url, {
|
|
414
|
-
params: queryParams,
|
|
415
|
-
});
|
|
416
|
-
return handleResult(response.data);
|
|
417
|
-
}
|
|
418
|
-
catch (error) {
|
|
419
|
-
return handleError(error);
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
mcpServer.tool('add-employer-manager', `Adding a manager`, {
|
|
423
|
-
employer_id: z.string(),
|
|
424
|
-
}, async (args) => {
|
|
425
|
-
try {
|
|
426
|
-
const { employer_id, ...requestData } = args;
|
|
427
|
-
const url = `/employers/${employer_id}/managers`;
|
|
428
|
-
const response = await apiClient.post(url, requestData);
|
|
429
|
-
return handleResult(response.data);
|
|
430
|
-
}
|
|
431
|
-
catch (error) {
|
|
432
|
-
return handleError(error);
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
mcpServer.tool('get-employer-manager-types', `Directory of manager types and privileges`, {
|
|
436
|
-
employer_id: z.string(),
|
|
437
|
-
}, async (args) => {
|
|
438
|
-
try {
|
|
439
|
-
const { employer_id, ...queryParams } = args;
|
|
440
|
-
const url = `/employers/${employer_id}/manager_types`;
|
|
441
|
-
const response = await apiClient.get(url, {
|
|
442
|
-
params: queryParams,
|
|
443
|
-
});
|
|
444
|
-
return handleResult(response.data);
|
|
445
|
-
}
|
|
446
|
-
catch (error) {
|
|
447
|
-
return handleError(error);
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
mcpServer.tool('get-manager-accounts', `Manager's work accounts`, {}, async (args) => {
|
|
451
|
-
try {
|
|
452
|
-
const response = await apiClient.get('/manager_accounts/mine', {
|
|
453
|
-
params: args,
|
|
454
|
-
});
|
|
455
|
-
return handleResult(response.data);
|
|
456
|
-
}
|
|
457
|
-
catch (error) {
|
|
458
|
-
return handleError(error);
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
mcpServer.tool('get-applicant-phone-info', `Get information about the applicant's phone number`, {
|
|
462
|
-
phone: z.string(),
|
|
463
|
-
}, async (args) => {
|
|
464
|
-
try {
|
|
465
|
-
const response = await apiClient.get('/resume_should_send_sms', {
|
|
466
|
-
params: args,
|
|
467
|
-
});
|
|
468
|
-
return handleResult(response.data);
|
|
469
|
-
}
|
|
470
|
-
catch (error) {
|
|
471
|
-
return handleError(error);
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
mcpServer.tool('get-address', `Get address by ID`, {
|
|
475
|
-
employer_id: z.string(),
|
|
476
|
-
address_id: z.string(),
|
|
477
|
-
with_manager: z.string().optional(),
|
|
478
|
-
}, async (args) => {
|
|
479
|
-
try {
|
|
480
|
-
const { employer_id, address_id, ...queryParams } = args;
|
|
481
|
-
const url = `/employers/${employer_id}/addresses/${address_id}`;
|
|
482
|
-
const response = await apiClient.get(url, {
|
|
483
|
-
params: queryParams,
|
|
484
|
-
});
|
|
485
|
-
return handleResult(response.data);
|
|
486
|
-
}
|
|
487
|
-
catch (error) {
|
|
488
|
-
return handleError(error);
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
async function main() {
|
|
492
|
-
const transport = new StdioServerTransport();
|
|
493
|
-
await mcpServer.server.connect(transport);
|
|
494
|
-
logger.log('HeadHunter API MCP Server started');
|
|
495
|
-
}
|
|
496
|
-
main().catch((error) => {
|
|
497
|
-
console.error('Server error:', error);
|
|
498
|
-
process.exit(1);
|
|
499
|
-
});
|