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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.js +112 -20
  3. 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 getDefaultParams(input) {
245
- if (input.year === 2025) {
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.6",
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.4",
50
+ "payrolla": "^0.2.6",
51
51
  "zod": "^3.25.0"
52
52
  },
53
53
  "devDependencies": {