@sargonpiraev/hh-mcp-server 1.9.0 → 1.11.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 +31 -99
- package/build/index.js +18 -72
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,133 +1,65 @@
|
|
|
1
1
|
# HeadHunter MCP Server
|
|
2
2
|
|
|
3
|
-

|
|
4
|
-

|
|
5
|
-
|
|
6
3
|
MCP Server for HeadHunter API - job search and vacancy management
|
|
7
4
|
|
|
8
|
-
##
|
|
9
|
-
|
|
10
|
-
- 🔍 User Profile Management - View and manage your HeadHunter profile
|
|
11
|
-
- 📄 Resume Management - View detailed resume information
|
|
12
|
-
- 💼 Job Application Management - Manage negotiations and communications with employers
|
|
13
|
-
- ⭐ Vacancy Search & Favorites - Search for jobs and manage your favorites
|
|
14
|
-
|
|
15
|
-
## Get Your Credentials
|
|
16
|
-
|
|
17
|
-
Before installation, you'll need to obtain your API credentials:
|
|
18
|
-
|
|
19
|
-
1. **HH_USER_AGENT**: User-Agent header for HeadHunter API requests (required by HH API)
|
|
20
|
-
|
|
21
|
-
Save these credentials for the installation steps below.
|
|
5
|
+
## Quick Start
|
|
22
6
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<details>
|
|
26
|
-
<summary><b>Installing via Smithery</b></summary>
|
|
27
|
-
|
|
28
|
-
To install HeadHunter MCP Server for any client automatically via [Smithery](https://smithery.ai):
|
|
7
|
+
1. Install dependencies:
|
|
29
8
|
|
|
30
9
|
```bash
|
|
31
|
-
|
|
10
|
+
npm install
|
|
32
11
|
```
|
|
33
12
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<details>
|
|
37
|
-
<summary><b>Install in Cursor</b></summary>
|
|
38
|
-
|
|
39
|
-
#### Cursor One-Click Installation
|
|
40
|
-
|
|
41
|
-
[](https://cursor.com/install-mcp?name=@sargonpiraev/hh-mcp-server&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzYXJnb25waXJhZXYvaGFiaXRpZnktbWNwLXNlcnZlciJdLCJlbnYiOnsiSEFCSVRJRllfQVBJX0tFWSI6InlvdXItaGFiaXRpZnktYXBpLWtleSJ9fQo=)
|
|
13
|
+
2. Set up environment:
|
|
42
14
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```json
|
|
48
|
-
{
|
|
49
|
-
"mcpServers": {
|
|
50
|
-
"hh-mcp-server": {
|
|
51
|
-
"command": "npx",
|
|
52
|
-
"args": ["-y", "@sargonpiraev/hh-mcp-server"],
|
|
53
|
-
"env": {
|
|
54
|
-
"HH_USER_AGENT": "your-HH_USER_AGENT-here"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
15
|
+
```bash
|
|
16
|
+
cp .env.example .env
|
|
17
|
+
# Edit .env with your API credentials
|
|
59
18
|
```
|
|
60
19
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<details>
|
|
64
|
-
<summary><b>Install in VS Code</b></summary>
|
|
65
|
-
|
|
66
|
-
[](vscode:mcp/install?%7B%22name%22%3A%22hh-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22@sargonpiraev/hh-mcp-server%22%5D%7D)
|
|
67
|
-
|
|
68
|
-
Or add manually to your VS Code settings:
|
|
20
|
+
3. Start the server:
|
|
69
21
|
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
"servers": {
|
|
73
|
-
"hh-mcp-server": {
|
|
74
|
-
"type": "stdio",
|
|
75
|
-
"command": "npx",
|
|
76
|
-
"args": ["-y", "@sargonpiraev/hh-mcp-server"],
|
|
77
|
-
"env": {
|
|
78
|
-
"HH_USER_AGENT": "your-HH_USER_AGENT-here"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
22
|
+
```bash
|
|
23
|
+
npm start
|
|
83
24
|
```
|
|
84
25
|
|
|
85
|
-
|
|
26
|
+
## Configuration
|
|
86
27
|
|
|
87
|
-
|
|
88
|
-
<summary><b>Install in Claude Desktop</b></summary>
|
|
89
|
-
|
|
90
|
-
Add to your `claude_desktop_config.json`:
|
|
28
|
+
Add to your MCP client config:
|
|
91
29
|
|
|
92
30
|
```json
|
|
93
31
|
{
|
|
94
32
|
"mcpServers": {
|
|
95
33
|
"hh-mcp-server": {
|
|
96
|
-
"command": "
|
|
97
|
-
"args": ["
|
|
34
|
+
"command": "node",
|
|
35
|
+
"args": ["/dist/index.js"],
|
|
98
36
|
"env": {
|
|
99
|
-
"HH_USER_AGENT": "
|
|
37
|
+
"HH_USER_AGENT": "your_hh_user_agent"
|
|
100
38
|
}
|
|
101
39
|
}
|
|
102
40
|
}
|
|
103
41
|
}
|
|
104
42
|
```
|
|
105
43
|
|
|
106
|
-
</details>
|
|
107
|
-
|
|
108
44
|
## Available Tools
|
|
109
45
|
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
46
|
+
- **get-vacancies**: Search for vacancies
|
|
47
|
+
- **get-dictionaries**: Directories of fields
|
|
48
|
+
- **get-areas**: Tree view of all regions
|
|
49
|
+
- **get-languages**: The list of all languages
|
|
50
|
+
- **get-industries**: Industries
|
|
51
|
+
- **get-metro**: The list of metro stations in all cities
|
|
52
|
+
- **get-professional-roles**: Professional role directory
|
|
53
|
+
- **get-employers**: Employer search
|
|
118
54
|
|
|
119
|
-
|
|
55
|
+
## Development
|
|
120
56
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
[](https://boosty.to/sargonpiraev)
|
|
57
|
+
```bash
|
|
58
|
+
npm run build # Build TypeScript
|
|
59
|
+
npm run test # Run tests
|
|
60
|
+
npm run lint # Check code style
|
|
61
|
+
```
|
|
128
62
|
|
|
129
|
-
##
|
|
63
|
+
## License
|
|
130
64
|
|
|
131
|
-
|
|
132
|
-
- 📧 Email: [sargonpiraev@gmail.com](mailto:sargonpiraev@gmail.com)
|
|
133
|
-
- 💬 Join [Discord](https://discord.gg/ZsWGxRGj)
|
|
65
|
+
MIT
|
package/build/index.js
CHANGED
|
@@ -4,15 +4,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import axios from 'axios';
|
|
6
6
|
import dotenv from 'dotenv';
|
|
7
|
-
// Load environment variables
|
|
8
7
|
dotenv.config();
|
|
9
|
-
// Environment configuration schema
|
|
10
8
|
const envSchema = z.object({
|
|
11
9
|
HH_USER_AGENT: z.string().min(1),
|
|
12
|
-
// TOOL_GLOB_PATTERNS: z.string().optional(),
|
|
13
|
-
// LOG_LEVEL: z.enum(['debug', 'info', 'notice', 'warning', 'error']).default('info').optional(),
|
|
14
10
|
});
|
|
15
|
-
// Parse and validate environment variables
|
|
16
11
|
const env = envSchema.parse(process.env);
|
|
17
12
|
const mcpServer = new McpServer({
|
|
18
13
|
name: '@sargonpiraev/hh-mcp-server',
|
|
@@ -29,7 +24,6 @@ const logger = {
|
|
|
29
24
|
error: (...message) => mcpServer.server.sendLoggingMessage({ level: 'error', data: message.join(' ') }),
|
|
30
25
|
debug: (...message) => mcpServer.server.sendLoggingMessage({ level: 'debug', data: message.join(' ') }),
|
|
31
26
|
};
|
|
32
|
-
// Axios client setup
|
|
33
27
|
const apiClient = axios.create({
|
|
34
28
|
baseURL: 'https://api.hh.ru',
|
|
35
29
|
headers: {
|
|
@@ -37,7 +31,6 @@ const apiClient = axios.create({
|
|
|
37
31
|
},
|
|
38
32
|
timeout: 30000,
|
|
39
33
|
});
|
|
40
|
-
// Add request interceptor for environment variables
|
|
41
34
|
apiClient.interceptors.request.use((config) => {
|
|
42
35
|
if (env.HH_USER_AGENT) {
|
|
43
36
|
config.headers['User-Agent'] = env.HH_USER_AGENT;
|
|
@@ -46,6 +39,16 @@ apiClient.interceptors.request.use((config) => {
|
|
|
46
39
|
}, (error) => {
|
|
47
40
|
return Promise.reject(error);
|
|
48
41
|
});
|
|
42
|
+
function handleResult(data) {
|
|
43
|
+
return {
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: 'text',
|
|
47
|
+
text: JSON.stringify(data, null, 2),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
49
52
|
function handleError(error) {
|
|
50
53
|
console.error(error);
|
|
51
54
|
logger.error('Error occurred:', JSON.stringify(error));
|
|
@@ -61,20 +64,12 @@ function handleError(error) {
|
|
|
61
64
|
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
62
65
|
};
|
|
63
66
|
}
|
|
64
|
-
// Tools
|
|
65
67
|
mcpServer.tool('get-vacancies', `Search for vacancies`, {}, async (args) => {
|
|
66
68
|
try {
|
|
67
69
|
const response = await apiClient.get('/vacancies', {
|
|
68
70
|
params: args,
|
|
69
71
|
});
|
|
70
|
-
return
|
|
71
|
-
content: [
|
|
72
|
-
{
|
|
73
|
-
type: 'text',
|
|
74
|
-
text: JSON.stringify(response.data, null, 2),
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
};
|
|
72
|
+
return handleResult(response.data);
|
|
78
73
|
}
|
|
79
74
|
catch (error) {
|
|
80
75
|
return handleError(error);
|
|
@@ -85,14 +80,7 @@ mcpServer.tool('get-dictionaries', `Directories of fields`, {}, async (args) =>
|
|
|
85
80
|
const response = await apiClient.get('/dictionaries', {
|
|
86
81
|
params: args,
|
|
87
82
|
});
|
|
88
|
-
return
|
|
89
|
-
content: [
|
|
90
|
-
{
|
|
91
|
-
type: 'text',
|
|
92
|
-
text: JSON.stringify(response.data, null, 2),
|
|
93
|
-
},
|
|
94
|
-
],
|
|
95
|
-
};
|
|
83
|
+
return handleResult(response.data);
|
|
96
84
|
}
|
|
97
85
|
catch (error) {
|
|
98
86
|
return handleError(error);
|
|
@@ -103,14 +91,7 @@ mcpServer.tool('get-areas', `Tree view of all regions`, {}, async (args) => {
|
|
|
103
91
|
const response = await apiClient.get('/areas', {
|
|
104
92
|
params: args,
|
|
105
93
|
});
|
|
106
|
-
return
|
|
107
|
-
content: [
|
|
108
|
-
{
|
|
109
|
-
type: 'text',
|
|
110
|
-
text: JSON.stringify(response.data, null, 2),
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
};
|
|
94
|
+
return handleResult(response.data);
|
|
114
95
|
}
|
|
115
96
|
catch (error) {
|
|
116
97
|
return handleError(error);
|
|
@@ -121,14 +102,7 @@ mcpServer.tool('get-languages', `The list of all languages`, {}, async (args) =>
|
|
|
121
102
|
const response = await apiClient.get('/languages', {
|
|
122
103
|
params: args,
|
|
123
104
|
});
|
|
124
|
-
return
|
|
125
|
-
content: [
|
|
126
|
-
{
|
|
127
|
-
type: 'text',
|
|
128
|
-
text: JSON.stringify(response.data, null, 2),
|
|
129
|
-
},
|
|
130
|
-
],
|
|
131
|
-
};
|
|
105
|
+
return handleResult(response.data);
|
|
132
106
|
}
|
|
133
107
|
catch (error) {
|
|
134
108
|
return handleError(error);
|
|
@@ -139,14 +113,7 @@ mcpServer.tool('get-industries', `Industries`, {}, async (args) => {
|
|
|
139
113
|
const response = await apiClient.get('/industries', {
|
|
140
114
|
params: args,
|
|
141
115
|
});
|
|
142
|
-
return
|
|
143
|
-
content: [
|
|
144
|
-
{
|
|
145
|
-
type: 'text',
|
|
146
|
-
text: JSON.stringify(response.data, null, 2),
|
|
147
|
-
},
|
|
148
|
-
],
|
|
149
|
-
};
|
|
116
|
+
return handleResult(response.data);
|
|
150
117
|
}
|
|
151
118
|
catch (error) {
|
|
152
119
|
return handleError(error);
|
|
@@ -157,14 +124,7 @@ mcpServer.tool('get-metro', `The list of metro stations in all cities`, {}, asyn
|
|
|
157
124
|
const response = await apiClient.get('/metro', {
|
|
158
125
|
params: args,
|
|
159
126
|
});
|
|
160
|
-
return
|
|
161
|
-
content: [
|
|
162
|
-
{
|
|
163
|
-
type: 'text',
|
|
164
|
-
text: JSON.stringify(response.data, null, 2),
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
};
|
|
127
|
+
return handleResult(response.data);
|
|
168
128
|
}
|
|
169
129
|
catch (error) {
|
|
170
130
|
return handleError(error);
|
|
@@ -175,14 +135,7 @@ mcpServer.tool('get-professional-roles', `Professional role directory`, {}, asyn
|
|
|
175
135
|
const response = await apiClient.get('/professional_roles', {
|
|
176
136
|
params: args,
|
|
177
137
|
});
|
|
178
|
-
return
|
|
179
|
-
content: [
|
|
180
|
-
{
|
|
181
|
-
type: 'text',
|
|
182
|
-
text: JSON.stringify(response.data, null, 2),
|
|
183
|
-
},
|
|
184
|
-
],
|
|
185
|
-
};
|
|
138
|
+
return handleResult(response.data);
|
|
186
139
|
}
|
|
187
140
|
catch (error) {
|
|
188
141
|
return handleError(error);
|
|
@@ -193,14 +146,7 @@ mcpServer.tool('get-employers', `Employer search`, {}, async (args) => {
|
|
|
193
146
|
const response = await apiClient.get('/employers', {
|
|
194
147
|
params: args,
|
|
195
148
|
});
|
|
196
|
-
return
|
|
197
|
-
content: [
|
|
198
|
-
{
|
|
199
|
-
type: 'text',
|
|
200
|
-
text: JSON.stringify(response.data, null, 2),
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
};
|
|
149
|
+
return handleResult(response.data);
|
|
204
150
|
}
|
|
205
151
|
catch (error) {
|
|
206
152
|
return handleError(error);
|