@t4dhg/mcp-factorial 1.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/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # MCP FactorialHR
2
+
3
+ > **Secure, privacy-focused access to FactorialHR data for AI assistants**
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+ [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-green.svg)](https://modelcontextprotocol.io/)
7
+ [![Node.js](https://img.shields.io/badge/Node.js-18%2B-brightgreen.svg)](https://nodejs.org/)
8
+
9
+ A Model Context Protocol (MCP) server that provides AI assistants like Claude with secure, read-only access to your FactorialHR employee and organizational data. Built with privacy and security as core principles.
10
+
11
+ ## Why This MCP Server?
12
+
13
+ - **Privacy-First Design**: Deliberately excludes payroll, compensation, and sensitive financial data. Your salary information stays private.
14
+ - **Read-Only Access**: No write operations - Claude can view but never modify your HR data.
15
+ - **Organizational Focus**: Optimized for team structure, employee directories, and location lookups - the data you actually need for AI-assisted workflows.
16
+ - **Enterprise Ready**: Built for companies who need AI integration without compromising data security.
17
+
18
+ ## Security by Design
19
+
20
+ This MCP server intentionally **does NOT expose**:
21
+ - Payroll and salary information
22
+ - Bank account details
23
+ - Tax documents
24
+ - Compensation packages
25
+ - Benefits enrollment data
26
+ - Personal identification numbers
27
+
28
+ We believe AI assistants should help with organizational tasks without having access to your most sensitive HR data.
29
+
30
+ ## Available Tools
31
+
32
+ | Tool | Description |
33
+ |------|-------------|
34
+ | `list_employees` | Get all employees with optional team/location filters |
35
+ | `get_employee` | Get employee details (name, role, contact, team assignments) |
36
+ | `search_employees` | Search employees by name or email |
37
+ | `list_teams` | View organizational team structure |
38
+ | `get_team` | Get team details and member list |
39
+ | `list_locations` | Get company office locations |
40
+ | `get_location` | Get location details (address, contact info) |
41
+ | `get_employee_contracts` | View job titles and employment dates |
42
+
43
+ ## Quick Start
44
+
45
+ ### 1. Add to your MCP configuration
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "factorial": {
51
+ "command": "npx",
52
+ "args": ["-y", "@t4dhg/mcp-factorial"]
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ ### 2. Set your API key
59
+
60
+ Create a `.env` file in your project root:
61
+
62
+ ```env
63
+ FACTORIAL_API_KEY=your-api-key-here
64
+ ```
65
+
66
+ Or pass it directly in the MCP config:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "factorial": {
72
+ "command": "npx",
73
+ "args": ["-y", "@t4dhg/mcp-factorial"],
74
+ "env": {
75
+ "FACTORIAL_API_KEY": "your-api-key-here"
76
+ }
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### 3. Start using it!
83
+
84
+ Once configured, ask Claude things like:
85
+
86
+ - *"Who's on the Engineering team?"*
87
+ - *"Find the email for John Smith"*
88
+ - *"What offices do we have?"*
89
+ - *"Show me the org structure"*
90
+
91
+ ## Getting an API Key
92
+
93
+ 1. Log in to [FactorialHR](https://app.factorialhr.com) as an administrator
94
+ 2. Navigate to **Settings → Integrations → API**
95
+ 3. Generate a new API key
96
+ 4. Add it to your `.env` file
97
+
98
+ > **Important**: API keys have full access to FactorialHR and never expire. Store them securely and rotate them periodically.
99
+
100
+ ## Use Cases
101
+
102
+ ### For Managers
103
+ - Quickly look up team member contact information
104
+ - Understand org chart and reporting structures
105
+ - Find employees by skill or department
106
+
107
+ ### For HR
108
+ - Power AI-assisted employee directory searches
109
+ - Streamline onboarding information lookups
110
+ - Support with organizational queries
111
+
112
+ ### For Developers
113
+ - Build AI workflows that need employee context
114
+ - Create custom Claude integrations
115
+ - Automate org-chart-aware processes
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ # Clone the repository
121
+ git clone https://github.com/t4dhg/mcp-factorial.git
122
+ cd mcp-factorial
123
+
124
+ # Install dependencies
125
+ npm install
126
+
127
+ # Build
128
+ npm run build
129
+
130
+ # Run locally
131
+ FACTORIAL_API_KEY=your-key npm start
132
+
133
+ # Test with MCP Inspector
134
+ npx @modelcontextprotocol/inspector
135
+ ```
136
+
137
+ ## Configuration Options
138
+
139
+ | Environment Variable | Description | Required |
140
+ |---------------------|-------------|----------|
141
+ | `FACTORIAL_API_KEY` | Your FactorialHR API key | Yes |
142
+ | `DEBUG` | Enable debug logging (`true`/`false`) | No |
143
+
144
+ ## Contributing
145
+
146
+ Contributions are welcome! Please feel free to submit a Pull Request.
147
+
148
+ ## License
149
+
150
+ MIT © [t4dhg](https://github.com/t4dhg)
151
+
152
+ ---
153
+
154
+ *Built with the [Model Context Protocol](https://modelcontextprotocol.io/) by Anthropic*
package/dist/api.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { Employee, Team, Location, Contract } from './types.js';
2
+ export declare function listEmployees(options?: {
3
+ team_id?: number;
4
+ location_id?: number;
5
+ }): Promise<Employee[]>;
6
+ export declare function getEmployee(id: number): Promise<Employee>;
7
+ export declare function searchEmployees(query: string): Promise<Employee[]>;
8
+ export declare function listTeams(): Promise<Team[]>;
9
+ export declare function getTeam(id: number): Promise<Team>;
10
+ export declare function listLocations(): Promise<Location[]>;
11
+ export declare function getLocation(id: number): Promise<Location>;
12
+ export declare function listContracts(employeeId?: number): Promise<Contract[]>;
13
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmGrE,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAGtB;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAM/D;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAcxE;AAGD,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAGjD;AAED,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMvD;AAGD,wBAAsB,aAAa,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAGzD;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAM/D;AAGD,wBAAsB,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAO5E"}
package/dist/api.js ADDED
@@ -0,0 +1,136 @@
1
+ // FactorialHR API Client
2
+ const FACTORIAL_BASE_URL = 'https://api.factorialhr.com/api/2025-10-01/resources';
3
+ const DEFAULT_TIMEOUT = 30000; // 30 seconds
4
+ const DEBUG = process.env.DEBUG === 'true';
5
+ function debug(message, data) {
6
+ if (DEBUG) {
7
+ const timestamp = new Date().toISOString();
8
+ if (data) {
9
+ console.error(`[${timestamp}] [mcp-factorial] ${message}`, JSON.stringify(data, null, 2));
10
+ }
11
+ else {
12
+ console.error(`[${timestamp}] [mcp-factorial] ${message}`);
13
+ }
14
+ }
15
+ }
16
+ function getApiKey() {
17
+ const apiKey = process.env.FACTORIAL_API_KEY;
18
+ if (!apiKey) {
19
+ throw new Error('FACTORIAL_API_KEY is not set. ' +
20
+ 'Please add it to your .env file or pass it via the MCP configuration. ' +
21
+ 'See https://github.com/t4dhg/mcp-factorial for setup instructions.');
22
+ }
23
+ return apiKey;
24
+ }
25
+ async function factorialFetch(endpoint, params) {
26
+ const url = new URL(`${FACTORIAL_BASE_URL}${endpoint}`);
27
+ if (params) {
28
+ Object.entries(params).forEach(([key, value]) => {
29
+ if (value !== undefined) {
30
+ url.searchParams.append(key, String(value));
31
+ }
32
+ });
33
+ }
34
+ debug(`Fetching: ${url.toString()}`);
35
+ const controller = new AbortController();
36
+ const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT);
37
+ try {
38
+ const response = await fetch(url.toString(), {
39
+ method: 'GET',
40
+ headers: {
41
+ 'x-api-key': getApiKey(),
42
+ 'Accept': 'application/json',
43
+ },
44
+ signal: controller.signal,
45
+ });
46
+ clearTimeout(timeoutId);
47
+ if (!response.ok) {
48
+ const errorText = await response.text();
49
+ debug(`API error (${response.status}):`, errorText);
50
+ // Provide user-friendly error messages
51
+ if (response.status === 401) {
52
+ throw new Error('Invalid API key. Please check your FACTORIAL_API_KEY.');
53
+ }
54
+ if (response.status === 403) {
55
+ throw new Error('Access denied. Your API key may not have permission for this operation.');
56
+ }
57
+ if (response.status === 404) {
58
+ throw new Error('Resource not found. The requested employee, team, or location may not exist.');
59
+ }
60
+ if (response.status === 429) {
61
+ throw new Error('Rate limit exceeded. Please wait a moment before trying again.');
62
+ }
63
+ throw new Error(`FactorialHR API error (${response.status}): ${errorText}`);
64
+ }
65
+ const data = await response.json();
66
+ debug('Response received', { endpoint, itemCount: Array.isArray(data) ? data.length : 1 });
67
+ return data;
68
+ }
69
+ catch (error) {
70
+ clearTimeout(timeoutId);
71
+ if (error instanceof Error) {
72
+ if (error.name === 'AbortError') {
73
+ throw new Error(`Request timed out after ${DEFAULT_TIMEOUT / 1000} seconds. Please try again.`);
74
+ }
75
+ throw error;
76
+ }
77
+ throw new Error('An unexpected error occurred while fetching data from FactorialHR.');
78
+ }
79
+ }
80
+ // Employee endpoints
81
+ export async function listEmployees(options) {
82
+ const data = await factorialFetch('/employees/employees', options);
83
+ return data.data || [];
84
+ }
85
+ export async function getEmployee(id) {
86
+ if (!id || id <= 0) {
87
+ throw new Error('Invalid employee ID. Please provide a positive number.');
88
+ }
89
+ const data = await factorialFetch(`/employees/employees/${id}`);
90
+ return data.data;
91
+ }
92
+ export async function searchEmployees(query) {
93
+ if (!query || query.trim().length < 2) {
94
+ throw new Error('Search query must be at least 2 characters long.');
95
+ }
96
+ const employees = await listEmployees();
97
+ const lowerQuery = query.toLowerCase().trim();
98
+ return employees.filter(emp => emp.full_name?.toLowerCase().includes(lowerQuery) ||
99
+ emp.email?.toLowerCase().includes(lowerQuery) ||
100
+ emp.first_name?.toLowerCase().includes(lowerQuery) ||
101
+ emp.last_name?.toLowerCase().includes(lowerQuery));
102
+ }
103
+ // Team endpoints
104
+ export async function listTeams() {
105
+ const data = await factorialFetch('/teams/teams');
106
+ return data.data || [];
107
+ }
108
+ export async function getTeam(id) {
109
+ if (!id || id <= 0) {
110
+ throw new Error('Invalid team ID. Please provide a positive number.');
111
+ }
112
+ const data = await factorialFetch(`/teams/teams/${id}`);
113
+ return data.data;
114
+ }
115
+ // Location endpoints
116
+ export async function listLocations() {
117
+ const data = await factorialFetch('/locations/locations');
118
+ return data.data || [];
119
+ }
120
+ export async function getLocation(id) {
121
+ if (!id || id <= 0) {
122
+ throw new Error('Invalid location ID. Please provide a positive number.');
123
+ }
124
+ const data = await factorialFetch(`/locations/locations/${id}`);
125
+ return data.data;
126
+ }
127
+ // Contract endpoints
128
+ export async function listContracts(employeeId) {
129
+ if (employeeId !== undefined && employeeId <= 0) {
130
+ throw new Error('Invalid employee ID. Please provide a positive number.');
131
+ }
132
+ const params = employeeId ? { employee_id: employeeId } : undefined;
133
+ const data = await factorialFetch('/contracts/contract-versions', params);
134
+ return data.data || [];
135
+ }
136
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Server for FactorialHR
4
+ *
5
+ * Provides access to employee and organizational data from FactorialHR
6
+ * through the Model Context Protocol for use with Claude Code and other MCP clients.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG"}
package/dist/index.js ADDED
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Server for FactorialHR
4
+ *
5
+ * Provides access to employee and organizational data from FactorialHR
6
+ * through the Model Context Protocol for use with Claude Code and other MCP clients.
7
+ */
8
+ import { config } from 'dotenv';
9
+ import { existsSync } from 'fs';
10
+ import { join } from 'path';
11
+ // Load environment variables from .env file
12
+ // Priority: ENV_FILE_PATH > cwd/.env > home/.env
13
+ function loadEnv() {
14
+ // 1. Check if explicit path provided
15
+ if (process.env.ENV_FILE_PATH && existsSync(process.env.ENV_FILE_PATH)) {
16
+ config({ path: process.env.ENV_FILE_PATH });
17
+ return;
18
+ }
19
+ // 2. Check current working directory
20
+ const cwdEnv = join(process.cwd(), '.env');
21
+ if (existsSync(cwdEnv)) {
22
+ config({ path: cwdEnv });
23
+ return;
24
+ }
25
+ // 3. Check home directory
26
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
27
+ const homeEnv = join(homeDir, '.env');
28
+ if (existsSync(homeEnv)) {
29
+ config({ path: homeEnv });
30
+ return;
31
+ }
32
+ // 4. Check common project locations
33
+ const commonPaths = [
34
+ join(homeDir, 'turborepo', '.env'),
35
+ join(homeDir, 'projects', '.env'),
36
+ ];
37
+ for (const envPath of commonPaths) {
38
+ if (existsSync(envPath)) {
39
+ config({ path: envPath });
40
+ return;
41
+ }
42
+ }
43
+ // Fall back to default dotenv behavior
44
+ config();
45
+ }
46
+ loadEnv();
47
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
48
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
49
+ import * as z from 'zod';
50
+ import { listEmployees, getEmployee, searchEmployees, listTeams, getTeam, listLocations, getLocation, listContracts, } from './api.js';
51
+ const server = new McpServer({
52
+ name: 'factorial-hr',
53
+ version: '1.0.0',
54
+ });
55
+ // ============================================================================
56
+ // Employee Tools
57
+ // ============================================================================
58
+ server.registerTool('list_employees', {
59
+ title: 'List Employees',
60
+ description: 'Get all employees from FactorialHR. Can filter by team or location.',
61
+ inputSchema: {
62
+ team_id: z.number().optional().describe('Filter by team ID'),
63
+ location_id: z.number().optional().describe('Filter by location ID'),
64
+ },
65
+ }, async ({ team_id, location_id }) => {
66
+ try {
67
+ const employees = await listEmployees({ team_id, location_id });
68
+ const summary = employees.map(e => ({
69
+ id: e.id,
70
+ name: e.full_name,
71
+ email: e.email,
72
+ role: e.role,
73
+ team_ids: e.team_ids,
74
+ location_id: e.location_id,
75
+ }));
76
+ return {
77
+ content: [
78
+ {
79
+ type: 'text',
80
+ text: `Found ${employees.length} employees:\n\n${JSON.stringify(summary, null, 2)}`,
81
+ },
82
+ ],
83
+ };
84
+ }
85
+ catch (error) {
86
+ return {
87
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
88
+ isError: true,
89
+ };
90
+ }
91
+ });
92
+ server.registerTool('get_employee', {
93
+ title: 'Get Employee',
94
+ description: 'Get detailed information about a specific employee by their ID.',
95
+ inputSchema: {
96
+ id: z.number().describe('The employee ID'),
97
+ },
98
+ }, async ({ id }) => {
99
+ try {
100
+ const employee = await getEmployee(id);
101
+ return {
102
+ content: [
103
+ {
104
+ type: 'text',
105
+ text: JSON.stringify(employee, null, 2),
106
+ },
107
+ ],
108
+ };
109
+ }
110
+ catch (error) {
111
+ return {
112
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
113
+ isError: true,
114
+ };
115
+ }
116
+ });
117
+ server.registerTool('search_employees', {
118
+ title: 'Search Employees',
119
+ description: 'Search for employees by name or email.',
120
+ inputSchema: {
121
+ query: z.string().describe('Search query (name or email)'),
122
+ },
123
+ }, async ({ query }) => {
124
+ try {
125
+ const employees = await searchEmployees(query);
126
+ if (employees.length === 0) {
127
+ return {
128
+ content: [{ type: 'text', text: `No employees found matching "${query}"` }],
129
+ };
130
+ }
131
+ const summary = employees.map(e => ({
132
+ id: e.id,
133
+ name: e.full_name,
134
+ email: e.email,
135
+ role: e.role,
136
+ }));
137
+ return {
138
+ content: [
139
+ {
140
+ type: 'text',
141
+ text: `Found ${employees.length} employees matching "${query}":\n\n${JSON.stringify(summary, null, 2)}`,
142
+ },
143
+ ],
144
+ };
145
+ }
146
+ catch (error) {
147
+ return {
148
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
149
+ isError: true,
150
+ };
151
+ }
152
+ });
153
+ // ============================================================================
154
+ // Team Tools
155
+ // ============================================================================
156
+ server.registerTool('list_teams', {
157
+ title: 'List Teams',
158
+ description: 'Get all teams in the organization.',
159
+ inputSchema: {},
160
+ }, async () => {
161
+ try {
162
+ const teams = await listTeams();
163
+ const summary = teams.map(t => ({
164
+ id: t.id,
165
+ name: t.name,
166
+ description: t.description,
167
+ employee_count: t.employee_ids?.length || 0,
168
+ }));
169
+ return {
170
+ content: [
171
+ {
172
+ type: 'text',
173
+ text: `Found ${teams.length} teams:\n\n${JSON.stringify(summary, null, 2)}`,
174
+ },
175
+ ],
176
+ };
177
+ }
178
+ catch (error) {
179
+ return {
180
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
181
+ isError: true,
182
+ };
183
+ }
184
+ });
185
+ server.registerTool('get_team', {
186
+ title: 'Get Team',
187
+ description: 'Get detailed information about a specific team by its ID.',
188
+ inputSchema: {
189
+ id: z.number().describe('The team ID'),
190
+ },
191
+ }, async ({ id }) => {
192
+ try {
193
+ const team = await getTeam(id);
194
+ return {
195
+ content: [
196
+ {
197
+ type: 'text',
198
+ text: JSON.stringify(team, null, 2),
199
+ },
200
+ ],
201
+ };
202
+ }
203
+ catch (error) {
204
+ return {
205
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
206
+ isError: true,
207
+ };
208
+ }
209
+ });
210
+ // ============================================================================
211
+ // Location Tools
212
+ // ============================================================================
213
+ server.registerTool('list_locations', {
214
+ title: 'List Locations',
215
+ description: 'Get all company locations.',
216
+ inputSchema: {},
217
+ }, async () => {
218
+ try {
219
+ const locations = await listLocations();
220
+ const summary = locations.map(l => ({
221
+ id: l.id,
222
+ name: l.name,
223
+ city: l.city,
224
+ country: l.country,
225
+ }));
226
+ return {
227
+ content: [
228
+ {
229
+ type: 'text',
230
+ text: `Found ${locations.length} locations:\n\n${JSON.stringify(summary, null, 2)}`,
231
+ },
232
+ ],
233
+ };
234
+ }
235
+ catch (error) {
236
+ return {
237
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
238
+ isError: true,
239
+ };
240
+ }
241
+ });
242
+ server.registerTool('get_location', {
243
+ title: 'Get Location',
244
+ description: 'Get detailed information about a specific location by its ID.',
245
+ inputSchema: {
246
+ id: z.number().describe('The location ID'),
247
+ },
248
+ }, async ({ id }) => {
249
+ try {
250
+ const location = await getLocation(id);
251
+ return {
252
+ content: [
253
+ {
254
+ type: 'text',
255
+ text: JSON.stringify(location, null, 2),
256
+ },
257
+ ],
258
+ };
259
+ }
260
+ catch (error) {
261
+ return {
262
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
263
+ isError: true,
264
+ };
265
+ }
266
+ });
267
+ // ============================================================================
268
+ // Contract Tools
269
+ // ============================================================================
270
+ server.registerTool('get_employee_contracts', {
271
+ title: 'Get Employee Contracts',
272
+ description: 'Get contract versions for an employee, including job title and effective date.',
273
+ inputSchema: {
274
+ employee_id: z.number().describe('The employee ID'),
275
+ },
276
+ }, async ({ employee_id }) => {
277
+ try {
278
+ const contracts = await listContracts(employee_id);
279
+ return {
280
+ content: [
281
+ {
282
+ type: 'text',
283
+ text: `Found ${contracts.length} contracts:\n\n${JSON.stringify(contracts, null, 2)}`,
284
+ },
285
+ ],
286
+ };
287
+ }
288
+ catch (error) {
289
+ return {
290
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` }],
291
+ isError: true,
292
+ };
293
+ }
294
+ });
295
+ // ============================================================================
296
+ // Start Server
297
+ // ============================================================================
298
+ async function main() {
299
+ const transport = new StdioServerTransport();
300
+ await server.connect(transport);
301
+ }
302
+ main().catch((error) => {
303
+ console.error('Fatal error:', error);
304
+ process.exit(1);
305
+ });
306
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,4CAA4C;AAC5C,iDAAiD;AACjD,SAAS,OAAO;IACd,qCAAqC;IACrC,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;QACvE,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;QAC5C,OAAO;IACT,CAAC;IAED,qCAAqC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IAED,0BAA0B;IAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;IAClE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,oCAAoC;IACpC,MAAM,WAAW,GAAG;QAClB,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC;QAClC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC;KAClC,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QAClC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YAC1B,OAAO;QACT,CAAC;IACH,CAAC;IAED,uCAAuC;IACvC,MAAM,EAAE,CAAC;AACX,CAAC;AAED,OAAO,EAAE,CAAC;AAEV,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EACL,aAAa,EACb,WAAW,EACX,eAAe,EACf,SAAS,EACT,OAAO,EACP,aAAa,EACb,WAAW,EACX,aAAa,GACd,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,cAAc;IACpB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;IACE,KAAK,EAAE,gBAAgB;IACvB,WAAW,EAAE,qEAAqE;IAClF,WAAW,EAAE;QACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAC5D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;KACrE;CACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE;IACjC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClC,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,SAAS;YACjB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,WAAW,EAAE,CAAC,CAAC,WAAW;SAC3B,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,SAAS,SAAS,CAAC,MAAM,kBAAkB,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACpF;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC;YACvG,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;IACE,KAAK,EAAE,cAAc;IACrB,WAAW,EAAE,iEAAiE;IAC9E,WAAW,EAAE;QACX,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;KAC3C;CACF,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;IACf,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;QACvC,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC;YACvG,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;IACE,KAAK,EAAE,kBAAkB;IACzB,WAAW,EAAE,wCAAwC;IACrD,WAAW,EAAE;QACX,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;KAC3D;CACF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;IAClB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gCAAgC,KAAK,GAAG,EAAE,CAAC;aAC5E,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClC,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,SAAS;YACjB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,IAAI,EAAE,CAAC,CAAC,IAAI;SACb,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,SAAS,SAAS,CAAC,MAAM,wBAAwB,KAAK,SAAS,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACxG;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC;YACvG,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E,MAAM,CAAC,YAAY,CACjB,YAAY,EACZ;IACE,KAAK,EAAE,YAAY;IACnB,WAAW,EAAE,oCAAoC;IACjD,WAAW,EAAE,EAAE;CAChB,EACD,KAAK,IAAI,EAAE;IACT,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC9B,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,cAAc,EAAE,CAAC,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC;SAC5C,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,SAAS,KAAK,CAAC,MAAM,cAAc,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBAC5E;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC;YACvG,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,UAAU,EACV;IACE,KAAK,EAAE,UAAU;IACjB,WAAW,EAAE,2DAA2D;IACxE,WAAW,EAAE;QACX,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;KACvC;CACF,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;IACf,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;iBACpC;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC;YACvG,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;IACE,KAAK,EAAE,gBAAgB;IACvB,WAAW,EAAE,4BAA4B;IACzC,WAAW,EAAE,EAAE;CAChB,EACD,KAAK,IAAI,EAAE;IACT,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,aAAa,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClC,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,OAAO;SACnB,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,SAAS,SAAS,CAAC,MAAM,kBAAkB,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACpF;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC;YACvG,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;IACE,KAAK,EAAE,cAAc;IACrB,WAAW,EAAE,+DAA+D;IAC5E,WAAW,EAAE;QACX,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;KAC3C;CACF,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;IACf,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;QACvC,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC;YACvG,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,MAAM,CAAC,YAAY,CACjB,wBAAwB,EACxB;IACE,KAAK,EAAE,wBAAwB;IAC/B,WAAW,EAAE,gFAAgF;IAC7F,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;KACpD;CACF,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;IACxB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;QACnD,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,SAAS,SAAS,CAAC,MAAM,kBAAkB,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACtF;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC;YACvG,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,58 @@
1
+ export interface Employee {
2
+ id: number;
3
+ first_name: string;
4
+ last_name: string;
5
+ full_name: string;
6
+ email: string;
7
+ birthday_on: string | null;
8
+ terminated_on: string | null;
9
+ gender: string | null;
10
+ nationality: string | null;
11
+ manager_id: number | null;
12
+ role: string | null;
13
+ timeoff_manager_id: number | null;
14
+ company_id: number;
15
+ legal_entity_id: number | null;
16
+ team_ids: number[];
17
+ location_id: number | null;
18
+ created_at: string;
19
+ updated_at: string;
20
+ }
21
+ export interface Team {
22
+ id: number;
23
+ name: string;
24
+ description: string | null;
25
+ company_id: number;
26
+ employee_ids: number[];
27
+ lead_ids: number[];
28
+ created_at: string;
29
+ updated_at: string;
30
+ }
31
+ export interface Location {
32
+ id: number;
33
+ name: string;
34
+ country: string | null;
35
+ phone_number: string | null;
36
+ state: string | null;
37
+ city: string | null;
38
+ address_line_1: string | null;
39
+ address_line_2: string | null;
40
+ postal_code: string | null;
41
+ company_id: number;
42
+ created_at: string;
43
+ updated_at: string;
44
+ }
45
+ export interface Contract {
46
+ id: number;
47
+ employee_id: number;
48
+ job_title: string | null;
49
+ effective_on: string;
50
+ created_at: string;
51
+ updated_at: string;
52
+ }
53
+ export interface ApiError {
54
+ error: string;
55
+ message: string;
56
+ status: number;
57
+ }
58
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // FactorialHR API Response Types
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,iCAAiC"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@t4dhg/mcp-factorial",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for FactorialHR - Access employee and organizational data from FactorialHR in Claude Code and other MCP clients",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "mcp-factorial": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/",
12
+ "README.md"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "start": "node dist/index.js",
23
+ "dev": "tsc --watch"
24
+ },
25
+ "keywords": [
26
+ "mcp",
27
+ "model-context-protocol",
28
+ "factorial",
29
+ "factorialhr",
30
+ "hr",
31
+ "claude",
32
+ "anthropic"
33
+ ],
34
+ "author": "t4dhg",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/t4dhg/mcp-factorial.git"
39
+ },
40
+ "dependencies": {
41
+ "@modelcontextprotocol/sdk": "^1.0.0",
42
+ "dotenv": "^16.4.0",
43
+ "zod": "^3.24.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^22.10.0",
47
+ "typescript": "^5.7.0"
48
+ },
49
+ "engines": {
50
+ "node": ">=18.0.0"
51
+ }
52
+ }