payrolla-mcp 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.js +112 -20
- package/package.json +2 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Payrolla
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,10 @@ function buildCustomGlobalParams(customParams) {
|
|
|
57
57
|
incomeTaxLimits: customParams.incomeTaxLimits
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
+
function buildSSICutDefList(customParams) {
|
|
61
|
+
if (!customParams?.ssiCutDefList) return void 0;
|
|
62
|
+
return customParams.ssiCutDefList;
|
|
63
|
+
}
|
|
60
64
|
async function calculatePayroll(client, input) {
|
|
61
65
|
const {
|
|
62
66
|
name,
|
|
@@ -102,7 +106,8 @@ async function calculatePayroll(client, input) {
|
|
|
102
106
|
payments,
|
|
103
107
|
calculationParams: {
|
|
104
108
|
calculateMinWageExemption: true,
|
|
105
|
-
customGlobalParams: buildCustomGlobalParams(customParams)
|
|
109
|
+
customGlobalParams: buildCustomGlobalParams(customParams),
|
|
110
|
+
ssiCutDefList: buildSSICutDefList(customParams)
|
|
106
111
|
}
|
|
107
112
|
};
|
|
108
113
|
let totalCost = 0;
|
|
@@ -214,6 +219,7 @@ async function calculateBulkPayroll(client, input) {
|
|
|
214
219
|
// src/types/index.ts
|
|
215
220
|
var DEFAULT_PARAMS_2025 = {
|
|
216
221
|
year: 2025,
|
|
222
|
+
month: 1,
|
|
217
223
|
minWage: 26005.5,
|
|
218
224
|
minWageNet: 22104.67,
|
|
219
225
|
ssiLowerLimit: 26005.5,
|
|
@@ -241,14 +247,46 @@ var DEFAULT_PARAMS_2025 = {
|
|
|
241
247
|
};
|
|
242
248
|
|
|
243
249
|
// src/tools/params.ts
|
|
244
|
-
function
|
|
245
|
-
if (
|
|
246
|
-
return DEFAULT_PARAMS_2025;
|
|
250
|
+
function getFallbackParams(year, month = 1) {
|
|
251
|
+
if (year === 2025) {
|
|
252
|
+
return { ...DEFAULT_PARAMS_2025, month };
|
|
253
|
+
}
|
|
254
|
+
return { ...DEFAULT_PARAMS_2025, year, month };
|
|
255
|
+
}
|
|
256
|
+
async function getDefaultParams(client, input) {
|
|
257
|
+
const month = input.month || 1;
|
|
258
|
+
try {
|
|
259
|
+
const response = await client.payroll.getParams(input.year, month);
|
|
260
|
+
if (response.isSuccess && response.data) {
|
|
261
|
+
const gp = response.data.globalParams;
|
|
262
|
+
const ssiCutDefs = response.data.ssiCutDefs;
|
|
263
|
+
return {
|
|
264
|
+
year: response.data.year,
|
|
265
|
+
month: response.data.month,
|
|
266
|
+
minWage: gp.minWage,
|
|
267
|
+
minWageNet: gp.minWageNet,
|
|
268
|
+
ssiLowerLimit: gp.ssi_LowerLimit,
|
|
269
|
+
ssiUpperLimit: gp.ssi_UpperLimit,
|
|
270
|
+
stampTaxRatio: gp.stampTaxRatio,
|
|
271
|
+
incomeTaxBrackets: (gp.incomeTaxLimits || []).map((b, i) => ({
|
|
272
|
+
limit: b.limit,
|
|
273
|
+
rate: b.rate,
|
|
274
|
+
description: `Bracket ${i + 1}`
|
|
275
|
+
})),
|
|
276
|
+
ssiCutDefList: ssiCutDefs ? ssiCutDefs.map((def) => ({
|
|
277
|
+
nr: def.nr,
|
|
278
|
+
name: def.name,
|
|
279
|
+
employerRatio: def.employerRatio,
|
|
280
|
+
employeeRatio: def.employeeRatio
|
|
281
|
+
})) : void 0
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
console.warn(`Failed to fetch params: ${response.message}`);
|
|
285
|
+
return getFallbackParams(input.year, month);
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error("Error fetching params from server:", error);
|
|
288
|
+
return getFallbackParams(input.year, month);
|
|
247
289
|
}
|
|
248
|
-
return {
|
|
249
|
-
...DEFAULT_PARAMS_2025,
|
|
250
|
-
year: input.year
|
|
251
|
-
};
|
|
252
290
|
}
|
|
253
291
|
function applyScenario(defaults, scenario) {
|
|
254
292
|
const result = {};
|
|
@@ -277,9 +315,29 @@ function applyRaise(wage, raisePercent) {
|
|
|
277
315
|
}
|
|
278
316
|
return wage * (1 + raisePercent / 100);
|
|
279
317
|
}
|
|
318
|
+
function getActiveParameters(baseParams, events, currentYear, currentMonth) {
|
|
319
|
+
const activeEvents = events.filter(
|
|
320
|
+
(e) => e.year < currentYear || e.year === currentYear && e.month <= currentMonth
|
|
321
|
+
).sort((a, b) => a.year - b.year || a.month - b.month);
|
|
322
|
+
let result = { ...baseParams };
|
|
323
|
+
for (const event of activeEvents) {
|
|
324
|
+
if (event.minWage !== void 0) result.minWage = event.minWage;
|
|
325
|
+
if (event.ssiLowerLimit !== void 0)
|
|
326
|
+
result.ssiLowerLimit = event.ssiLowerLimit;
|
|
327
|
+
if (event.ssiUpperLimit !== void 0)
|
|
328
|
+
result.ssiUpperLimit = event.ssiUpperLimit;
|
|
329
|
+
if (event.stampTaxRatio !== void 0)
|
|
330
|
+
result.stampTaxRatio = event.stampTaxRatio;
|
|
331
|
+
if (event.incomeTaxLimits !== void 0)
|
|
332
|
+
result.incomeTaxLimits = event.incomeTaxLimits;
|
|
333
|
+
if (event.ssiCutDefList !== void 0)
|
|
334
|
+
result.ssiCutDefList = event.ssiCutDefList;
|
|
335
|
+
}
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
280
338
|
async function simulateBudget(client, input) {
|
|
281
|
-
const { employees, year, periodCount, scenario } = input;
|
|
282
|
-
const defaults = getDefaultParams({ year });
|
|
339
|
+
const { employees, year, periodCount, scenario, parameterEvents } = input;
|
|
340
|
+
const defaults = await getDefaultParams(client, { year });
|
|
283
341
|
const customParams = applyScenario(defaults, scenario);
|
|
284
342
|
const employeeResults = [];
|
|
285
343
|
let totalYearlyCost = 0;
|
|
@@ -294,6 +352,7 @@ async function simulateBudget(client, input) {
|
|
|
294
352
|
let empTotalCost = 0;
|
|
295
353
|
let empTotalNet = 0;
|
|
296
354
|
let empTotalGross = 0;
|
|
355
|
+
const empPeriods = [];
|
|
297
356
|
for (let i = 0; i < periodCount; i++) {
|
|
298
357
|
const calcDate = new Date(year, i, 1);
|
|
299
358
|
const calcYear = calcDate.getFullYear();
|
|
@@ -307,6 +366,12 @@ async function simulateBudget(client, input) {
|
|
|
307
366
|
type: pe.type,
|
|
308
367
|
paymentType: pe.paymentType
|
|
309
368
|
}));
|
|
369
|
+
const effectiveParams = getActiveParameters(
|
|
370
|
+
customParams,
|
|
371
|
+
parameterEvents || [],
|
|
372
|
+
calcYear,
|
|
373
|
+
calcMonth
|
|
374
|
+
);
|
|
310
375
|
const result = await calculatePayroll(client, {
|
|
311
376
|
name: emp.name,
|
|
312
377
|
wage: adjustedWage,
|
|
@@ -316,7 +381,7 @@ async function simulateBudget(client, input) {
|
|
|
316
381
|
month: calcMonth,
|
|
317
382
|
periodCount: 1,
|
|
318
383
|
extraPayments: extraPayments.length > 0 ? extraPayments : void 0,
|
|
319
|
-
customParams,
|
|
384
|
+
customParams: effectiveParams,
|
|
320
385
|
cumulativeIncomeTaxBase,
|
|
321
386
|
cumulativeMinWageIncomeTaxBase,
|
|
322
387
|
transferredSSIBase1,
|
|
@@ -326,6 +391,7 @@ async function simulateBudget(client, input) {
|
|
|
326
391
|
empTotalNet += result.totalNet;
|
|
327
392
|
empTotalGross += result.totalGross;
|
|
328
393
|
const lastPeriod = result.periods[0];
|
|
394
|
+
empPeriods.push(lastPeriod);
|
|
329
395
|
cumulativeIncomeTaxBase = lastPeriod.cumulativeIncomeTaxBase;
|
|
330
396
|
cumulativeMinWageIncomeTaxBase = lastPeriod.cumulativeMinWageIncomeTaxBase;
|
|
331
397
|
transferredSSIBase1 = lastPeriod.transferredSSIBase1;
|
|
@@ -337,7 +403,8 @@ async function simulateBudget(client, input) {
|
|
|
337
403
|
adjustedWage,
|
|
338
404
|
yearlyCost: empTotalCost,
|
|
339
405
|
yearlyNet: empTotalNet,
|
|
340
|
-
yearlyGross: empTotalGross
|
|
406
|
+
yearlyGross: empTotalGross,
|
|
407
|
+
periods: empPeriods
|
|
341
408
|
});
|
|
342
409
|
totalYearlyCost += empTotalCost;
|
|
343
410
|
totalYearlyNet += empTotalNet;
|
|
@@ -363,7 +430,7 @@ async function simulateBudget(client, input) {
|
|
|
363
430
|
};
|
|
364
431
|
}
|
|
365
432
|
async function compareScenarios(client, input) {
|
|
366
|
-
const { employees, year, periodCount, scenarios } = input;
|
|
433
|
+
const { employees, year, periodCount, scenarios, parameterEvents } = input;
|
|
367
434
|
if (scenarios.length === 0) {
|
|
368
435
|
throw new Error("At least one scenario is required");
|
|
369
436
|
}
|
|
@@ -373,7 +440,8 @@ async function compareScenarios(client, input) {
|
|
|
373
440
|
employees,
|
|
374
441
|
year,
|
|
375
442
|
periodCount,
|
|
376
|
-
scenario
|
|
443
|
+
scenario,
|
|
444
|
+
parameterEvents
|
|
377
445
|
});
|
|
378
446
|
results.push({
|
|
379
447
|
name: scenario.name || `Scenario ${results.length + 1}`,
|
|
@@ -602,6 +670,26 @@ var PayEventSchema = z2.object({
|
|
|
602
670
|
z2.enum(["RegularPayment", "Overtime", "SocialAid", "ExtraPay"])
|
|
603
671
|
]).optional().describe("Payment category: 1/RegularPayment, 2/Overtime, 3/SocialAid, 4/ExtraPay (default: 4)")
|
|
604
672
|
});
|
|
673
|
+
var SSICutDefSchema = z2.object({
|
|
674
|
+
nr: z2.number().describe("Order number for the cut"),
|
|
675
|
+
name: z2.string().describe("Name of the SSI cut"),
|
|
676
|
+
employerRatio: z2.number().describe("Employer contribution ratio (e.g., 0.02 for 2%)"),
|
|
677
|
+
employeeRatio: z2.number().describe("Employee contribution ratio (e.g., 0.03 for 3%)")
|
|
678
|
+
});
|
|
679
|
+
var ParameterEventSchema = z2.object({
|
|
680
|
+
month: z2.number().min(1).max(12).describe("Month when parameter change takes effect (1-12)"),
|
|
681
|
+
year: z2.number().describe("Year when parameter change takes effect"),
|
|
682
|
+
name: z2.string().optional().describe("Description of the parameter change"),
|
|
683
|
+
minWage: z2.number().optional().describe("New minimum wage"),
|
|
684
|
+
ssiLowerLimit: z2.number().optional().describe("New SSI lower limit"),
|
|
685
|
+
ssiUpperLimit: z2.number().optional().describe("New SSI upper limit"),
|
|
686
|
+
stampTaxRatio: z2.number().optional().describe("New stamp tax ratio"),
|
|
687
|
+
incomeTaxLimits: z2.array(z2.object({
|
|
688
|
+
limit: z2.number(),
|
|
689
|
+
rate: z2.number()
|
|
690
|
+
})).optional().describe("New income tax brackets"),
|
|
691
|
+
ssiCutDefList: z2.array(SSICutDefSchema).optional().describe("Custom SSI cut definitions (replaces defaults from this month onwards)")
|
|
692
|
+
});
|
|
605
693
|
var CustomParamsSchema = z2.object({
|
|
606
694
|
minWage: z2.number().optional().describe("Custom minimum wage (gross)"),
|
|
607
695
|
minWageNet: z2.number().optional().describe("Custom minimum wage (net)"),
|
|
@@ -611,7 +699,8 @@ var CustomParamsSchema = z2.object({
|
|
|
611
699
|
incomeTaxLimits: z2.array(z2.object({
|
|
612
700
|
limit: z2.number().describe("Upper limit for this bracket"),
|
|
613
701
|
rate: z2.number().describe("Tax rate (e.g., 0.15 for 15%)")
|
|
614
|
-
})).optional().describe("Custom income tax brackets")
|
|
702
|
+
})).optional().describe("Custom income tax brackets"),
|
|
703
|
+
ssiCutDefList: z2.array(SSICutDefSchema).optional().describe("Custom SSI cut definitions")
|
|
615
704
|
});
|
|
616
705
|
var EmployeeInputSchema = z2.object({
|
|
617
706
|
name: z2.string().describe("Employee name"),
|
|
@@ -745,7 +834,8 @@ function registerTools(server, client) {
|
|
|
745
834
|
})).describe("Array of employees"),
|
|
746
835
|
year: z2.number().describe("Calculation year"),
|
|
747
836
|
periodCount: z2.number().min(1).max(12).describe("Number of months (use 12 for yearly)"),
|
|
748
|
-
scenario: ScenarioConfigSchema.describe("Scenario configuration with changes to apply")
|
|
837
|
+
scenario: ScenarioConfigSchema.describe("Scenario configuration with changes to apply"),
|
|
838
|
+
parameterEvents: z2.array(ParameterEventSchema).optional().describe("Parameter changes that take effect from a specific month onwards (applies to all employees). Unlike payEvents, these persist for subsequent months.")
|
|
749
839
|
},
|
|
750
840
|
async (params) => {
|
|
751
841
|
try {
|
|
@@ -784,7 +874,8 @@ function registerTools(server, client) {
|
|
|
784
874
|
})).describe("Array of employees"),
|
|
785
875
|
year: z2.number().describe("Calculation year"),
|
|
786
876
|
periodCount: z2.number().min(1).max(12).describe("Number of months"),
|
|
787
|
-
scenarios: z2.array(ScenarioConfigSchema).min(1).describe("Array of scenarios to compare")
|
|
877
|
+
scenarios: z2.array(ScenarioConfigSchema).min(1).describe("Array of scenarios to compare"),
|
|
878
|
+
parameterEvents: z2.array(ParameterEventSchema).optional().describe("Parameter changes that take effect from a specific month onwards (applies to all employees)")
|
|
788
879
|
},
|
|
789
880
|
async (params) => {
|
|
790
881
|
try {
|
|
@@ -812,13 +903,14 @@ function registerTools(server, client) {
|
|
|
812
903
|
);
|
|
813
904
|
server.tool(
|
|
814
905
|
"get_default_params",
|
|
815
|
-
"Get default Turkish payroll parameters for a given year",
|
|
906
|
+
"Get default Turkish payroll parameters for a given year and month",
|
|
816
907
|
{
|
|
817
|
-
year: z2.number().describe("Year to get parameters for (e.g., 2025)")
|
|
908
|
+
year: z2.number().describe("Year to get parameters for (e.g., 2025)"),
|
|
909
|
+
month: z2.number().min(1).max(12).optional().describe("Month to get parameters for (1-12, default: 1)")
|
|
818
910
|
},
|
|
819
911
|
async (params) => {
|
|
820
912
|
try {
|
|
821
|
-
const result = getDefaultParams(params);
|
|
913
|
+
const result = await getDefaultParams(client, params);
|
|
822
914
|
return {
|
|
823
915
|
content: [
|
|
824
916
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payrolla-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "MCP server for Payrolla payroll budget simulations - enables LLMs to calculate Turkish payroll and simulate budget scenarios",
|
|
5
5
|
"author": "Payrolla",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
50
|
-
"payrolla": "^0.2.
|
|
50
|
+
"payrolla": "^0.2.6",
|
|
51
51
|
"zod": "^3.25.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|