@t4dhg/mcp-factorial 7.1.0 → 8.2.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 +278 -55
- package/dist/api/ats.d.ts +78 -0
- package/dist/api/ats.d.ts.map +1 -0
- package/dist/api/ats.js +173 -0
- package/dist/api/ats.js.map +1 -0
- package/dist/api/attendance.d.ts +27 -0
- package/dist/api/attendance.d.ts.map +1 -0
- package/dist/api/attendance.js +62 -0
- package/dist/api/attendance.js.map +1 -0
- package/dist/api/contracts.d.ts +40 -0
- package/dist/api/contracts.d.ts.map +1 -0
- package/dist/api/contracts.js +146 -0
- package/dist/api/contracts.js.map +1 -0
- package/dist/api/documents.d.ts +95 -0
- package/dist/api/documents.d.ts.map +1 -0
- package/dist/api/documents.js +265 -0
- package/dist/api/documents.js.map +1 -0
- package/dist/api/employees.d.ts +35 -0
- package/dist/api/employees.d.ts.map +1 -0
- package/dist/api/employees.js +116 -0
- package/dist/api/employees.js.map +1 -0
- package/dist/api/index.d.ts +22 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +36 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/job-catalog.d.ts +21 -0
- package/dist/api/job-catalog.d.ts.map +1 -0
- package/dist/api/job-catalog.js +34 -0
- package/dist/api/job-catalog.js.map +1 -0
- package/dist/api/locations.d.ts +26 -0
- package/dist/api/locations.d.ts.map +1 -0
- package/dist/api/locations.js +59 -0
- package/dist/api/locations.js.map +1 -0
- package/dist/api/payroll.d.ts +30 -0
- package/dist/api/payroll.d.ts.map +1 -0
- package/dist/api/payroll.js +68 -0
- package/dist/api/payroll.js.map +1 -0
- package/dist/api/projects.d.ts +82 -0
- package/dist/api/projects.d.ts.map +1 -0
- package/dist/api/projects.js +193 -0
- package/dist/api/projects.js.map +1 -0
- package/dist/api/shared.d.ts +12 -0
- package/dist/api/shared.d.ts.map +1 -0
- package/dist/api/shared.js +17 -0
- package/dist/api/shared.js.map +1 -0
- package/dist/api/teams.d.ts +26 -0
- package/dist/api/teams.d.ts.map +1 -0
- package/dist/api/teams.js +59 -0
- package/dist/api/teams.js.map +1 -0
- package/dist/api/time-off.d.ts +47 -0
- package/dist/api/time-off.d.ts.map +1 -0
- package/dist/api/time-off.js +112 -0
- package/dist/api/time-off.js.map +1 -0
- package/dist/api/training.d.ts +62 -0
- package/dist/api/training.d.ts.map +1 -0
- package/dist/api/training.js +147 -0
- package/dist/api/training.js.map +1 -0
- package/dist/api/work-areas.d.ts +30 -0
- package/dist/api/work-areas.d.ts.map +1 -0
- package/dist/api/work-areas.js +71 -0
- package/dist/api/work-areas.js.map +1 -0
- package/dist/api.d.ts +3 -464
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +3 -1274
- package/dist/api.js.map +1 -1
- package/dist/audit.d.ts +8 -3
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +8 -3
- package/dist/audit.js.map +1 -1
- package/dist/index.d.ts +6 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3860
- package/dist/index.js.map +1 -1
- package/dist/oauth.d.ts +63 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +143 -0
- package/dist/oauth.js.map +1 -0
- package/dist/schemas/ats.d.ts +326 -0
- package/dist/schemas/ats.d.ts.map +1 -0
- package/dist/schemas/ats.js +121 -0
- package/dist/schemas/ats.js.map +1 -0
- package/dist/schemas/documents.d.ts +78 -0
- package/dist/schemas/documents.d.ts.map +1 -0
- package/dist/schemas/documents.js +34 -0
- package/dist/schemas/documents.js.map +1 -0
- package/dist/schemas/employees.d.ts +528 -0
- package/dist/schemas/employees.d.ts.map +1 -0
- package/dist/schemas/employees.js +206 -0
- package/dist/schemas/employees.js.map +1 -0
- package/dist/schemas/index.d.ts +18 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +28 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/job-catalog.d.ts +57 -0
- package/dist/schemas/job-catalog.d.ts.map +1 -0
- package/dist/schemas/job-catalog.js +27 -0
- package/dist/schemas/job-catalog.js.map +1 -0
- package/dist/schemas/payroll.d.ts +98 -0
- package/dist/schemas/payroll.d.ts.map +1 -0
- package/dist/schemas/payroll.js +43 -0
- package/dist/schemas/payroll.js.map +1 -0
- package/dist/schemas/projects.d.ts +277 -0
- package/dist/schemas/projects.d.ts.map +1 -0
- package/dist/schemas/projects.js +114 -0
- package/dist/schemas/projects.js.map +1 -0
- package/dist/schemas/shared.d.ts +42 -0
- package/dist/schemas/shared.d.ts.map +1 -0
- package/dist/schemas/shared.js +65 -0
- package/dist/schemas/shared.js.map +1 -0
- package/dist/schemas/time-off.d.ts +286 -0
- package/dist/schemas/time-off.d.ts.map +1 -0
- package/dist/schemas/time-off.js +111 -0
- package/dist/schemas/time-off.js.map +1 -0
- package/dist/schemas/training.d.ts +223 -0
- package/dist/schemas/training.d.ts.map +1 -0
- package/dist/schemas/training.js +88 -0
- package/dist/schemas/training.js.map +1 -0
- package/dist/schemas/work-areas.d.ts +71 -0
- package/dist/schemas/work-areas.d.ts.map +1 -0
- package/dist/schemas/work-areas.js +30 -0
- package/dist/schemas/work-areas.js.map +1 -0
- package/dist/schemas.d.ts +3 -1932
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +3 -785
- package/dist/schemas.js.map +1 -1
- package/dist/tool-utils.d.ts +0 -14
- package/dist/tool-utils.d.ts.map +1 -1
- package/dist/tool-utils.js +5 -18
- package/dist/tool-utils.js.map +1 -1
- package/dist/tools/ats.d.ts +6 -0
- package/dist/tools/ats.d.ts.map +1 -0
- package/dist/tools/ats.js +206 -0
- package/dist/tools/ats.js.map +1 -0
- package/dist/tools/attendance.d.ts +6 -0
- package/dist/tools/attendance.d.ts.map +1 -0
- package/dist/tools/attendance.js +86 -0
- package/dist/tools/attendance.js.map +1 -0
- package/dist/tools/contracts.d.ts +6 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +63 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/documents.d.ts +6 -0
- package/dist/tools/documents.d.ts.map +1 -0
- package/dist/tools/documents.js +135 -0
- package/dist/tools/documents.js.map +1 -0
- package/dist/tools/employees.d.ts +6 -0
- package/dist/tools/employees.d.ts.map +1 -0
- package/dist/tools/employees.js +125 -0
- package/dist/tools/employees.js.map +1 -0
- package/dist/tools/index.d.ts +17 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +221 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/job-catalog.d.ts +6 -0
- package/dist/tools/job-catalog.d.ts.map +1 -0
- package/dist/tools/job-catalog.js +39 -0
- package/dist/tools/job-catalog.js.map +1 -0
- package/dist/tools/locations.d.ts +6 -0
- package/dist/tools/locations.d.ts.map +1 -0
- package/dist/tools/locations.js +93 -0
- package/dist/tools/locations.js.map +1 -0
- package/dist/tools/payroll.d.ts +6 -0
- package/dist/tools/payroll.d.ts.map +1 -0
- package/dist/tools/payroll.js +76 -0
- package/dist/tools/payroll.js.map +1 -0
- package/dist/tools/projects.d.ts +6 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +203 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/shared.d.ts +84 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +164 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/teams.d.ts +6 -0
- package/dist/tools/teams.d.ts.map +1 -0
- package/dist/tools/teams.js +81 -0
- package/dist/tools/teams.js.map +1 -0
- package/dist/tools/time-off.d.ts +6 -0
- package/dist/tools/time-off.d.ts.map +1 -0
- package/dist/tools/time-off.js +144 -0
- package/dist/tools/time-off.js.map +1 -0
- package/dist/tools/training.d.ts +6 -0
- package/dist/tools/training.d.ts.map +1 -0
- package/dist/tools/training.js +160 -0
- package/dist/tools/training.js.map +1 -0
- package/dist/tools/work-areas.d.ts +6 -0
- package/dist/tools/work-areas.d.ts.map +1 -0
- package/dist/tools/work-areas.js +85 -0
- package/dist/tools/work-areas.js.map +1 -0
- package/dist/utils.d.ts +35 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +97 -0
- package/dist/utils.js.map +1 -1
- package/dist/write-safety.d.ts +229 -2
- package/dist/write-safety.d.ts.map +1 -1
- package/dist/write-safety.js +42 -3
- package/dist/write-safety.js.map +1 -1
- package/llms.txt +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/t4dhg/mcp-factorial/main/assets/factorial-logo.svg" alt="FactorialHR" height="48">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# MCP FactorialHR
|
|
2
6
|
|
|
3
|
-
> **The definitive Model Context Protocol server for FactorialHR**
|
|
7
|
+
> **The definitive Model Context Protocol server for [FactorialHR](https://factorialhr.com)**
|
|
4
8
|
|
|
5
9
|
[](https://opensource.org/licenses/MIT)
|
|
6
10
|
[](https://github.com/t4dhg/mcp-factorial/actions/workflows/ci.yml)
|
|
@@ -10,37 +14,82 @@
|
|
|
10
14
|
[](https://nodejs.org/)
|
|
11
15
|
[](https://www.npmjs.com/package/@t4dhg/mcp-factorial)
|
|
12
16
|
[](https://modelcontextprotocol.io/)
|
|
13
|
-
[](https://smithery.ai/server/@t4dhg/mcp-factorial)
|
|
14
17
|
|
|
15
18
|
A comprehensive Model Context Protocol (MCP) server that provides AI assistants like Claude with full access to FactorialHR. Manage employees, teams, time off, projects, training, recruiting, and more - all with built-in safety guardrails.
|
|
16
19
|
|
|
17
20
|
## Why This MCP Server?
|
|
18
21
|
|
|
19
|
-
- **
|
|
22
|
+
- **Context-Optimized**: 14 hierarchical tools (117 operations) with 88% less context usage than individual tools
|
|
20
23
|
- **Full CRUD Operations**: Create, read, update, and delete across all major entities
|
|
21
24
|
- **Safety Guardrails**: High-risk operations require explicit confirmation
|
|
22
|
-
- **Audit Logging**: All write operations are logged for
|
|
25
|
+
- **Audit Logging**: All write operations are logged with timestamps and context for debugging
|
|
23
26
|
- **Enterprise Ready**: Built for companies who need AI integration with proper controls
|
|
24
27
|
|
|
25
28
|
## Features
|
|
26
29
|
|
|
27
|
-
###
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
30
|
+
### Hierarchical Tool Discovery (v8.0.0+)
|
|
31
|
+
|
|
32
|
+
The MCP server uses a hierarchical tool structure for optimal context usage. Instead of 117 individual tools, you get 14 category-based tools with an `action` parameter.
|
|
33
|
+
|
|
34
|
+
| Tool | Description | Actions |
|
|
35
|
+
| ----------------------- | ----------------------------- | -------------------------------------------------- |
|
|
36
|
+
| `factorial_discover` | Discover available categories | - |
|
|
37
|
+
| `factorial_employees` | Employee management | list, get, search, create, update, terminate |
|
|
38
|
+
| `factorial_teams` | Team management | list, get, create, update, delete |
|
|
39
|
+
| `factorial_locations` | Location management | list, get, create, update, delete |
|
|
40
|
+
| `factorial_contracts` | Contract/salary data | list, get_with_employee, by_job_role, by_job_level |
|
|
41
|
+
| `factorial_time_off` | Leave management | 10 actions |
|
|
42
|
+
| `factorial_attendance` | Shift management | list, get, create, update, delete |
|
|
43
|
+
| `factorial_documents` | Document management | 8 actions (downloads require OAuth2 - see below) |
|
|
44
|
+
| `factorial_job_catalog` | Job roles/levels | list_roles, get_role, list_levels |
|
|
45
|
+
| `factorial_projects` | Project management | 16 actions for projects, tasks, workers, time |
|
|
46
|
+
| `factorial_training` | Training management | 12 actions for trainings, sessions, enrollments |
|
|
47
|
+
| `factorial_work_areas` | Work area management | list, get, create, update, archive, unarchive |
|
|
48
|
+
| `factorial_ats` | Applicant tracking | 17 actions for recruiting |
|
|
49
|
+
| `factorial_payroll` | Payroll data (read-only) | 6 actions |
|
|
50
|
+
|
|
51
|
+
**Example Usage:**
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// List all employees
|
|
55
|
+
factorial_employees({ action: 'list', page: 1, limit: 50 });
|
|
56
|
+
|
|
57
|
+
// Get a specific employee
|
|
58
|
+
factorial_employees({ action: 'get', id: 123 });
|
|
59
|
+
|
|
60
|
+
// Search employees
|
|
61
|
+
factorial_employees({ action: 'search', query: 'john' });
|
|
62
|
+
|
|
63
|
+
// Create a leave request
|
|
64
|
+
factorial_time_off({
|
|
65
|
+
action: 'create',
|
|
66
|
+
employee_id: 123,
|
|
67
|
+
leave_type_id: 1,
|
|
68
|
+
start_on: '2026-02-01',
|
|
69
|
+
finish_on: '2026-02-05',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Discover available actions for a category
|
|
73
|
+
factorial_discover({ category: 'employees' });
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 117 Operations Across 14 Categories
|
|
77
|
+
|
|
78
|
+
| Category | Operations |
|
|
79
|
+
| --------------- | ------------------------------------------------------------------------------------------------------ |
|
|
80
|
+
| **Employees** | list, get, search, create, update, terminate |
|
|
81
|
+
| **Teams** | list, get, create, update, delete |
|
|
82
|
+
| **Locations** | list, get, create, update, delete |
|
|
83
|
+
| **Time Off** | list_leaves, get_leave, list_types, get_type, list_allowances, create, update, cancel, approve, reject |
|
|
84
|
+
| **Attendance** | list, get, create, update, delete |
|
|
85
|
+
| **Projects** | 16 operations for projects, tasks, workers, time records |
|
|
86
|
+
| **Training** | 12 operations for trainings, sessions, enrollments |
|
|
87
|
+
| **Work Areas** | list, get, create, update, archive, unarchive |
|
|
88
|
+
| **ATS** | 17 operations for job postings, candidates, applications, hiring stages |
|
|
89
|
+
| **Payroll** | list/get supplements, tax identifiers, family situations (read-only) |
|
|
90
|
+
| **Documents** | 8 operations for folders, documents, and downloads (⚠️ downloads require OAuth2) |
|
|
91
|
+
| **Job Catalog** | list_roles, get_role, list_levels (read-only) |
|
|
92
|
+
| **Contracts** | list, get_with_employee, by_job_role, by_job_level (read-only) |
|
|
44
93
|
|
|
45
94
|
### 5 MCP Resources
|
|
46
95
|
|
|
@@ -64,7 +113,7 @@ A comprehensive Model Context Protocol (MCP) server that provides AI assistants
|
|
|
64
113
|
### Architecture Features
|
|
65
114
|
|
|
66
115
|
- **Safety Guardrails**: High-risk operations (terminate, delete) marked for confirmation
|
|
67
|
-
- **Audit Logging**: All write operations logged with timestamps and context
|
|
116
|
+
- **Audit Logging**: All write operations logged in-process with timestamps and context
|
|
68
117
|
- **Caching**: In-memory TTL-based caching (configurable by resource type)
|
|
69
118
|
- **Pagination**: All list operations support pagination
|
|
70
119
|
- **Retry Logic**: Exponential backoff with rate limit handling
|
|
@@ -135,6 +184,87 @@ You'll need a FactorialHR API key to use this MCP server. Here's how to get one:
|
|
|
135
184
|
|
|
136
185
|
> **Important**: API keys have full access to your FactorialHR data and never expire. Store them securely, never commit them to version control, and rotate them periodically.
|
|
137
186
|
|
|
187
|
+
## OAuth2 Setup (Required for Document Downloads)
|
|
188
|
+
|
|
189
|
+
Document download actions (`download_payslips`, `download`) **require OAuth2 authentication**. This is a Factorial API limitation - the download endpoint does not accept API key authentication.
|
|
190
|
+
|
|
191
|
+
> **Note**: You need admin access in Factorial to create OAuth applications.
|
|
192
|
+
|
|
193
|
+
### Step 1: Create an OAuth2 Application
|
|
194
|
+
|
|
195
|
+
1. Go to: **https://api.factorialhr.com/oauth/applications**
|
|
196
|
+
2. Click **"New application"**
|
|
197
|
+
3. Fill in:
|
|
198
|
+
- **Redirect URI**: `http://localhost:8080/callback` (or any URL you can access)
|
|
199
|
+
- **Confidentiality**: Yes (server application)
|
|
200
|
+
- **Scopes**: Select the scopes you need:
|
|
201
|
+
- **Required for downloads**: Documents, Employees
|
|
202
|
+
- **Recommended for full MCP functionality**: Contracts, Payroll, Payroll supplements, Time off, Shift management, Trainings, Recruitment, Company locations, Job catalog
|
|
203
|
+
4. Save and note your **Client ID** and **Client Secret**
|
|
204
|
+
|
|
205
|
+
### Step 2: Get Authorization Code
|
|
206
|
+
|
|
207
|
+
Open this URL in your browser (replace `YOUR_CLIENT_ID`):
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
https://api.factorialhr.com/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8080/callback&response_type=code
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- Log in and authorize the app
|
|
214
|
+
- You'll be redirected to your callback URL with `?code=AUTHORIZATION_CODE`
|
|
215
|
+
- Copy that code from the URL (it expires quickly, so proceed to step 3 immediately)
|
|
216
|
+
|
|
217
|
+
### Step 3: Exchange Code for Tokens
|
|
218
|
+
|
|
219
|
+
Run this curl command (replace placeholders):
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
curl -X POST 'https://api.factorialhr.com/oauth/token' \
|
|
223
|
+
-d 'client_id=YOUR_CLIENT_ID' \
|
|
224
|
+
-d 'client_secret=YOUR_CLIENT_SECRET' \
|
|
225
|
+
-d 'code=AUTHORIZATION_CODE' \
|
|
226
|
+
-d 'grant_type=authorization_code' \
|
|
227
|
+
-d 'redirect_uri=http://localhost:8080/callback'
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
You'll get a response with `access_token` and `refresh_token`. Save the **refresh_token**.
|
|
231
|
+
|
|
232
|
+
### Step 4: Configure MCP Server
|
|
233
|
+
|
|
234
|
+
Add OAuth2 credentials to your MCP configuration:
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"mcpServers": {
|
|
239
|
+
"factorial": {
|
|
240
|
+
"command": "npx",
|
|
241
|
+
"args": ["-y", "@t4dhg/mcp-factorial"],
|
|
242
|
+
"env": {
|
|
243
|
+
"FACTORIAL_API_KEY": "your-api-key",
|
|
244
|
+
"FACTORIAL_OAUTH_CLIENT_ID": "your-client-id",
|
|
245
|
+
"FACTORIAL_OAUTH_CLIENT_SECRET": "your-client-secret",
|
|
246
|
+
"FACTORIAL_OAUTH_REFRESH_TOKEN": "your-refresh-token"
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Or add to your `.env` file:
|
|
254
|
+
|
|
255
|
+
```env
|
|
256
|
+
FACTORIAL_API_KEY=your-api-key
|
|
257
|
+
FACTORIAL_OAUTH_CLIENT_ID=your-client-id
|
|
258
|
+
FACTORIAL_OAUTH_CLIENT_SECRET=your-client-secret
|
|
259
|
+
FACTORIAL_OAUTH_REFRESH_TOKEN=your-refresh-token
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Important Notes
|
|
263
|
+
|
|
264
|
+
- **Refresh tokens expire after 1 week** - you'll need to repeat steps 2-3 if it expires
|
|
265
|
+
- The MCP server automatically refreshes access tokens using the refresh token
|
|
266
|
+
- If document downloads suddenly stop working, your refresh token has likely expired
|
|
267
|
+
|
|
138
268
|
## Use Cases
|
|
139
269
|
|
|
140
270
|
### For Managers
|
|
@@ -162,60 +292,86 @@ You'll need a FactorialHR API key to use this MCP server. Here's how to get one:
|
|
|
162
292
|
|
|
163
293
|
## Configuration Options
|
|
164
294
|
|
|
165
|
-
| Environment Variable
|
|
166
|
-
|
|
|
167
|
-
| `FACTORIAL_API_KEY`
|
|
168
|
-
| `FACTORIAL_API_VERSION`
|
|
169
|
-
| `FACTORIAL_TIMEOUT_MS`
|
|
170
|
-
| `FACTORIAL_MAX_RETRIES`
|
|
171
|
-
| `DEBUG`
|
|
295
|
+
| Environment Variable | Description | Default |
|
|
296
|
+
| ------------------------------- | ------------------------------------ | ------------ |
|
|
297
|
+
| `FACTORIAL_API_KEY` | Your FactorialHR API key | Required |
|
|
298
|
+
| `FACTORIAL_API_VERSION` | API version | `2025-10-01` |
|
|
299
|
+
| `FACTORIAL_TIMEOUT_MS` | Request timeout (ms) | `30000` |
|
|
300
|
+
| `FACTORIAL_MAX_RETRIES` | Max retry attempts | `3` |
|
|
301
|
+
| `DEBUG` | Enable debug logging | `false` |
|
|
302
|
+
| `FACTORIAL_OAUTH_CLIENT_ID` | OAuth2 client ID (for downloads) | - |
|
|
303
|
+
| `FACTORIAL_OAUTH_CLIENT_SECRET` | OAuth2 client secret (for downloads) | - |
|
|
304
|
+
| `FACTORIAL_OAUTH_REFRESH_TOKEN` | OAuth2 refresh token (for downloads) | - |
|
|
172
305
|
|
|
173
306
|
## Safety & Security
|
|
174
307
|
|
|
175
|
-
###
|
|
308
|
+
### Operations That Require Confirmation
|
|
176
309
|
|
|
177
|
-
The following operations
|
|
310
|
+
The following operations require explicit confirmation (`confirm: true`). Called without it, the tool returns a warning describing the impact and makes no change. Risk is classified per operation in `src/write-safety.ts`; everything below is gated regardless of whether it is rated high or medium:
|
|
178
311
|
|
|
179
|
-
- `
|
|
180
|
-
- `
|
|
181
|
-
- `
|
|
182
|
-
- `
|
|
183
|
-
- `
|
|
312
|
+
- `factorial_employees({ action: 'terminate' })` - Terminates an employee
|
|
313
|
+
- `factorial_teams({ action: 'delete' })` - Permanently deletes a team
|
|
314
|
+
- `factorial_locations({ action: 'delete' })` - Permanently deletes a location
|
|
315
|
+
- `factorial_projects({ action: 'delete' })` - Permanently deletes a project
|
|
316
|
+
- `factorial_projects({ action: 'delete_task' })` - Deletes a project task
|
|
317
|
+
- `factorial_projects({ action: 'delete_time' })` - Deletes a time record
|
|
318
|
+
- `factorial_projects({ action: 'remove_worker' })` - Deletes an employee's assignment to a project
|
|
319
|
+
- `factorial_attendance({ action: 'delete' })` - Deletes a shift record
|
|
320
|
+
- `factorial_time_off({ action: 'cancel' })` - Cancels a leave request
|
|
321
|
+
- `factorial_time_off({ action: 'reject' })` - Rejects a leave request
|
|
322
|
+
- `factorial_training({ action: 'delete' })` - Deletes a training program and its enrollments
|
|
323
|
+
- `factorial_training({ action: 'delete_session' })` - Deletes a training session
|
|
324
|
+
- `factorial_training({ action: 'unenroll' })` - Deletes an employee's training enrollment
|
|
325
|
+
- `factorial_ats({ action: 'delete_posting' })` - Deletes a job posting and its applications
|
|
326
|
+
- `factorial_ats({ action: 'delete_candidate' })` - Permanently deletes a candidate
|
|
327
|
+
- `factorial_ats({ action: 'delete_application' })` - Permanently deletes an application
|
|
328
|
+
|
|
329
|
+
### Document Downloads
|
|
330
|
+
|
|
331
|
+
Document names come from Factorial metadata rather than from the caller, so downloads treat them as untrusted:
|
|
332
|
+
|
|
333
|
+
- The name is reduced to a single path segment, so a name containing path separators cannot redirect the download outside `output_dir`.
|
|
334
|
+
- Control characters are dropped and over-long names are shortened, keeping the extension.
|
|
335
|
+
- **Nothing is ever overwritten.** If the target name is already taken, the file is saved as `name (1).ext`, `name (2).ext` and so on. This protects existing files in `output_dir` and stops two documents that share a name from collapsing into one.
|
|
336
|
+
|
|
337
|
+
Downloads still write wherever you point `output_dir`, so point it at a directory meant for downloads rather than a source tree or your home directory.
|
|
184
338
|
|
|
185
339
|
### Read-Only Categories
|
|
186
340
|
|
|
187
341
|
Some categories are intentionally read-only for security:
|
|
188
342
|
|
|
189
343
|
- **Payroll**: Supplements, tax identifiers, family situations
|
|
190
|
-
- **Documents**: Folder and document metadata
|
|
344
|
+
- **Documents**: Folder and document metadata (download tools available for payslips and documents)
|
|
191
345
|
- **Contracts**: Historical contract data
|
|
192
346
|
|
|
193
|
-
### Response Optimization
|
|
347
|
+
### Response Optimization
|
|
194
348
|
|
|
195
|
-
|
|
349
|
+
Document and contract list operations return **summary format** by default to prevent token overflow:
|
|
196
350
|
|
|
197
|
-
**Documents** (`
|
|
351
|
+
**Documents** (`factorial_documents({ action: 'list' })`):
|
|
198
352
|
|
|
199
|
-
- Returns: `id`, `name`, `folder_id`, `employee_id`, `
|
|
200
|
-
- Default limit:
|
|
201
|
-
- For full details: Use `
|
|
353
|
+
- Returns: `id`, `name`, `folder_id`, `employee_id`, `mime_type` (5 fields)
|
|
354
|
+
- Default limit: 100 documents per page
|
|
355
|
+
- For full details: Use `factorial_documents({ action: 'get', id: X })` for complete metadata
|
|
202
356
|
|
|
203
|
-
**Contracts** (`
|
|
357
|
+
**Contracts** (`factorial_contracts({ action: 'list' })`):
|
|
204
358
|
|
|
205
359
|
- Returns: `id`, `employee_id`, `job_title`, `effective_on` (4 fields)
|
|
206
|
-
- Default limit:
|
|
207
|
-
- For full details: Timestamps (`created_at`, `updated_at`) excluded from summary
|
|
360
|
+
- Default limit: 100 contracts per page
|
|
208
361
|
|
|
209
|
-
|
|
362
|
+
All list operations accept `page` and `limit` parameters for pagination control.
|
|
210
363
|
|
|
211
364
|
### Audit Logging
|
|
212
365
|
|
|
213
|
-
All write operations (create, update, delete, approve, reject) are
|
|
366
|
+
All write operations (create, update, delete, approve, reject) are recorded with:
|
|
214
367
|
|
|
215
368
|
- Timestamp
|
|
216
369
|
- Operation type
|
|
217
370
|
- Entity type and ID
|
|
218
371
|
- Changes made
|
|
372
|
+
- Success or failure, and duration
|
|
373
|
+
|
|
374
|
+
**Scope of this log.** The trail is held in memory in the running server process, capped at the most recent 1000 entries, and is not exposed through any tool or resource. It is lost when the process exits. Set `DEBUG=true` to have each entry written to the server's stderr, which is the only way to retain it today. It is a debugging aid, not a compliance record: if you need a durable, queryable audit trail, use FactorialHR's own activity log as the system of record.
|
|
219
375
|
|
|
220
376
|
## Development
|
|
221
377
|
|
|
@@ -249,6 +405,42 @@ FACTORIAL_API_KEY=your-key npm start
|
|
|
249
405
|
npx @modelcontextprotocol/inspector
|
|
250
406
|
```
|
|
251
407
|
|
|
408
|
+
### Project Structure
|
|
409
|
+
|
|
410
|
+
The codebase is organized into domain-based modules for maintainability:
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
src/
|
|
414
|
+
├── schemas/ # Zod schemas by domain
|
|
415
|
+
│ ├── employees.ts # Employee, Team, Location, Contract schemas
|
|
416
|
+
│ ├── time-off.ts # Leave, LeaveType, Allowance, Shift schemas
|
|
417
|
+
│ ├── projects.ts # Project, Task, Worker, TimeRecord schemas
|
|
418
|
+
│ ├── training.ts # Training, Session, Membership schemas
|
|
419
|
+
│ ├── ats.ts # JobPosting, Candidate, Application schemas
|
|
420
|
+
│ └── ...
|
|
421
|
+
├── api/ # API functions by domain
|
|
422
|
+
│ ├── employees.ts # listEmployees, getEmployee, createEmployee, etc.
|
|
423
|
+
│ ├── time-off.ts # listLeaves, createLeave, approveLeave, etc.
|
|
424
|
+
│ ├── projects.ts # listProjects, createProject, etc.
|
|
425
|
+
│ └── ...
|
|
426
|
+
├── tools/ # MCP tool registrations by domain
|
|
427
|
+
│ ├── employees.ts # factorial_employees tool registration
|
|
428
|
+
│ ├── time-off.ts # factorial_time_off tool registration
|
|
429
|
+
│ ├── index.ts # Server setup, discovery tool, resources, prompts
|
|
430
|
+
│ └── ...
|
|
431
|
+
├── index.ts # Entry point (re-exports from tools/)
|
|
432
|
+
├── api.ts # Re-exports from api/
|
|
433
|
+
└── schemas.ts # Re-exports from schemas/
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Adding a new feature:**
|
|
437
|
+
|
|
438
|
+
1. Add schemas to `src/schemas/{domain}.ts`
|
|
439
|
+
2. Add API functions to `src/api/{domain}.ts`
|
|
440
|
+
3. Add tool actions to `src/tools/{domain}.ts`
|
|
441
|
+
4. Update `src/schemas/index.ts`, `src/api/index.ts` exports if needed
|
|
442
|
+
5. Run `npm test` to verify
|
|
443
|
+
|
|
252
444
|
## Troubleshooting
|
|
253
445
|
|
|
254
446
|
### API Key Not Working
|
|
@@ -271,6 +463,36 @@ The server implements exponential backoff for rate limits. If you're hitting lim
|
|
|
271
463
|
- **Team membership**: Some employees may not be assigned to teams
|
|
272
464
|
- **Empty responses**: Check if the resource exists in your Factorial account
|
|
273
465
|
|
|
466
|
+
### Document Downloads Not Working
|
|
467
|
+
|
|
468
|
+
Document downloads **require OAuth2 authentication**. This is a Factorial API limitation - the download endpoint does not accept API key authentication.
|
|
469
|
+
|
|
470
|
+
If you see an error like:
|
|
471
|
+
|
|
472
|
+
> "Document download requires OAuth2 authentication"
|
|
473
|
+
|
|
474
|
+
You need to set up OAuth2 credentials. See [OAuth2 Setup](#oauth2-setup-required-for-document-downloads) above.
|
|
475
|
+
|
|
476
|
+
**Note**: OAuth2 refresh tokens expire after 1 week. If downloads suddenly stop working, re-authorize and get a new refresh token.
|
|
477
|
+
|
|
478
|
+
### "Document with ID X not found" Error
|
|
479
|
+
|
|
480
|
+
The Factorial API's individual document endpoint (`GET /documents/{id}`) has limitations accessing employee-specific documents. This happens because:
|
|
481
|
+
|
|
482
|
+
1. `list_documents` with `employee_ids` filter correctly returns all employee documents
|
|
483
|
+
2. `get_document` by ID cannot access those same documents individually
|
|
484
|
+
|
|
485
|
+
**Workaround**: Use `download_payslips` action instead of `download` action. The `download_payslips` action uses the document metadata from the list operation directly, bypassing the problematic individual GET endpoint:
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
// This works - uses document list internally
|
|
489
|
+
factorial_documents({
|
|
490
|
+
action: 'download_payslips',
|
|
491
|
+
employee_id: 123,
|
|
492
|
+
output_dir: '/path/to/downloads',
|
|
493
|
+
});
|
|
494
|
+
```
|
|
495
|
+
|
|
274
496
|
## FAQ
|
|
275
497
|
|
|
276
498
|
**Q: Does this expose salary/payroll data?**
|
|
@@ -286,7 +508,7 @@ A: Data is cached in-memory with TTLs: employees (5 min), teams (10 min), locati
|
|
|
286
508
|
A: Version `2025-10-01` by default. Override with `FACTORIAL_API_VERSION` environment variable.
|
|
287
509
|
|
|
288
510
|
**Q: Are write operations logged?**
|
|
289
|
-
A: Yes,
|
|
511
|
+
A: Yes, every write is recorded by the audit module with a timestamp, entity, changes, and outcome. The log lives in memory in the running process (last 1000 entries) and is not retrievable through the MCP interface, so treat it as a debugging aid rather than a compliance record. See [Audit Logging](#audit-logging).
|
|
290
512
|
|
|
291
513
|
## Factorial API Quirks and Limitations
|
|
292
514
|
|
|
@@ -303,12 +525,13 @@ The FactorialHR API has some design patterns that differ from typical REST APIs.
|
|
|
303
525
|
|
|
304
526
|
### Endpoint Quirks
|
|
305
527
|
|
|
306
|
-
| Endpoint | Quirk
|
|
307
|
-
| ------------------------------ |
|
|
308
|
-
| `GET /employees/{id}` | May return 404 for valid employees
|
|
309
|
-
| `GET /documents/{id}` | May return 404 for
|
|
310
|
-
| `GET /contracts?employee_id=X` | Filtering unreliable
|
|
311
|
-
| Empty results | Returns `{"errors": null}` instead of `{"data": []}
|
|
528
|
+
| Endpoint | Quirk | Workaround |
|
|
529
|
+
| ------------------------------ | --------------------------------------------------- | ---------------------------------------------- |
|
|
530
|
+
| `GET /employees/{id}` | May return 404 for valid employees | Server falls back to listing all and filtering |
|
|
531
|
+
| `GET /documents/{id}` | May return 404 for employee-specific documents | Use `download_payslips` which bypasses this |
|
|
532
|
+
| `GET /contracts?employee_id=X` | Filtering unreliable | Server fetches all and filters client-side |
|
|
533
|
+
| Empty results | Returns `{"errors": null}` instead of `{"data": []} | Server handles both formats |
|
|
534
|
+
| Document download URLs | Requires OAuth2 (API key does not work) | Configure OAuth2 credentials for downloads |
|
|
312
535
|
|
|
313
536
|
### Field Availability
|
|
314
537
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ATS (Applicant Tracking System) API endpoints
|
|
3
|
+
*/
|
|
4
|
+
import { type PaginatedResponse, type PaginationInput } from '../pagination.js';
|
|
5
|
+
import type { JobPosting, Candidate, Application, HiringStage, CreateJobPostingInput, UpdateJobPostingInput, CreateCandidateInput, UpdateCandidateInput, CreateApplicationInput, UpdateApplicationInput } from '../schemas.js';
|
|
6
|
+
/**
|
|
7
|
+
* List all job postings
|
|
8
|
+
*/
|
|
9
|
+
export declare function listJobPostings(options?: PaginationInput): Promise<PaginatedResponse<JobPosting>>;
|
|
10
|
+
/**
|
|
11
|
+
* Get a specific job posting by ID
|
|
12
|
+
*/
|
|
13
|
+
export declare function getJobPosting(id: number): Promise<JobPosting>;
|
|
14
|
+
/**
|
|
15
|
+
* List all candidates
|
|
16
|
+
*/
|
|
17
|
+
export declare function listCandidates(options?: PaginationInput): Promise<PaginatedResponse<Candidate>>;
|
|
18
|
+
/**
|
|
19
|
+
* Get a specific candidate by ID
|
|
20
|
+
*/
|
|
21
|
+
export declare function getCandidate(id: number): Promise<Candidate>;
|
|
22
|
+
/**
|
|
23
|
+
* List all applications
|
|
24
|
+
*/
|
|
25
|
+
export declare function listApplications(jobPostingId?: number, options?: PaginationInput): Promise<PaginatedResponse<Application>>;
|
|
26
|
+
/**
|
|
27
|
+
* Get a specific application by ID
|
|
28
|
+
*/
|
|
29
|
+
export declare function getApplication(id: number): Promise<Application>;
|
|
30
|
+
/**
|
|
31
|
+
* List all hiring stages
|
|
32
|
+
*/
|
|
33
|
+
export declare function listHiringStages(): Promise<HiringStage[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Get a specific hiring stage by ID
|
|
36
|
+
*/
|
|
37
|
+
export declare function getHiringStage(id: number): Promise<HiringStage>;
|
|
38
|
+
/**
|
|
39
|
+
* Create a job posting
|
|
40
|
+
*/
|
|
41
|
+
export declare function createJobPosting(input: CreateJobPostingInput): Promise<JobPosting>;
|
|
42
|
+
/**
|
|
43
|
+
* Update a job posting
|
|
44
|
+
*/
|
|
45
|
+
export declare function updateJobPosting(id: number, input: UpdateJobPostingInput): Promise<JobPosting>;
|
|
46
|
+
/**
|
|
47
|
+
* Delete a job posting
|
|
48
|
+
*/
|
|
49
|
+
export declare function deleteJobPosting(id: number): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Create a candidate
|
|
52
|
+
*/
|
|
53
|
+
export declare function createCandidate(input: CreateCandidateInput): Promise<Candidate>;
|
|
54
|
+
/**
|
|
55
|
+
* Update a candidate
|
|
56
|
+
*/
|
|
57
|
+
export declare function updateCandidate(id: number, input: UpdateCandidateInput): Promise<Candidate>;
|
|
58
|
+
/**
|
|
59
|
+
* Delete a candidate
|
|
60
|
+
*/
|
|
61
|
+
export declare function deleteCandidate(id: number): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Create an application
|
|
64
|
+
*/
|
|
65
|
+
export declare function createApplication(input: CreateApplicationInput): Promise<Application>;
|
|
66
|
+
/**
|
|
67
|
+
* Update an application
|
|
68
|
+
*/
|
|
69
|
+
export declare function updateApplication(id: number, input: UpdateApplicationInput): Promise<Application>;
|
|
70
|
+
/**
|
|
71
|
+
* Delete an application
|
|
72
|
+
*/
|
|
73
|
+
export declare function deleteApplication(id: number): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Advance an application to the next stage
|
|
76
|
+
*/
|
|
77
|
+
export declare function advanceApplication(id: number): Promise<Application>;
|
|
78
|
+
//# sourceMappingURL=ats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ats.d.ts","sourceRoot":"","sources":["../../src/api/ats.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,eAAe,CAAC;AASvB;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAQxC;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAOnE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAIvC;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAGjE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CASzC;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAGrE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAM/D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAGrE;AAMD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAMxF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,UAAU,CAAC,CASrB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,SAAS,CAAC,CAIrF;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,SAAS,CAAC,CAMjG;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC,CAI3F;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,WAAW,CAAC,CAMtB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMjE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAMzE"}
|