@attendance-engine/mcp 0.1.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/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/chunk-UACYGW4C.js +489 -0
- package/dist/chunk-UACYGW4C.js.map +1 -0
- package/dist/cli.cjs +501 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +491 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
5
|
+
var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
6
|
+
var core = require('@attendance-engine/core');
|
|
7
|
+
var zod = require('zod');
|
|
8
|
+
|
|
9
|
+
var PunchSchema = zod.z.object({
|
|
10
|
+
at: zod.z.string().describe('ISO-8601 instant with an explicit offset, e.g. "2026-06-01T08:57:00+06:00".'),
|
|
11
|
+
source: zod.z.enum(["biometric", "mobile", "manual", "web"]).optional(),
|
|
12
|
+
location: zod.z.string().optional()
|
|
13
|
+
});
|
|
14
|
+
var BreakWindowSchema = zod.z.object({
|
|
15
|
+
start: zod.z.string().regex(/^\d{2}:\d{2}$/),
|
|
16
|
+
end: zod.z.string().regex(/^\d{2}:\d{2}$/),
|
|
17
|
+
paid: zod.z.boolean()
|
|
18
|
+
});
|
|
19
|
+
var ShiftConfigSchema = zod.z.object({
|
|
20
|
+
start: zod.z.string().regex(/^\d{2}:\d{2}$/).describe("Scheduled start, HH:MM, worksite local wall-clock."),
|
|
21
|
+
end: zod.z.string().regex(/^\d{2}:\d{2}$/).describe("Scheduled end, HH:MM. If <= start, the shift is overnight and ends on the next day."),
|
|
22
|
+
breaks: zod.z.array(BreakWindowSchema).optional(),
|
|
23
|
+
graceIn: zod.z.number().int().min(0).optional(),
|
|
24
|
+
graceOut: zod.z.number().int().min(0).optional(),
|
|
25
|
+
minHalfDayMinutes: zod.z.number().int().min(0).optional(),
|
|
26
|
+
flexible: zod.z.boolean().optional()
|
|
27
|
+
});
|
|
28
|
+
var AttendancePolicySchema = zod.z.object({
|
|
29
|
+
pairing: zod.z.enum(["first-last", "in-out-pairs"]).optional(),
|
|
30
|
+
treatMissingOutAs: zod.z.enum(["absent", "shift-end", "half-day", "flag-only"]).optional(),
|
|
31
|
+
midnightCutover: zod.z.enum(["shift-anchored", "calendar-day"]).optional(),
|
|
32
|
+
lateAfterGrace: zod.z.enum(["mark-late", "deduct", "ignore"]).optional(),
|
|
33
|
+
otThresholdMinutes: zod.z.number().int().min(0).optional(),
|
|
34
|
+
otRoundingUnit: zod.z.number().int().min(1).optional(),
|
|
35
|
+
otMode: zod.z.enum(["shift-based", "fixed-hours", "daily-cap"]).optional(),
|
|
36
|
+
standardDayMinutes: zod.z.number().int().min(0).optional(),
|
|
37
|
+
dedupeSeconds: zod.z.number().int().min(0).optional(),
|
|
38
|
+
tzOffsetMinutes: zod.z.number().int().optional()
|
|
39
|
+
});
|
|
40
|
+
var LeaveDaySchema = zod.z.object({
|
|
41
|
+
type: zod.z.string().optional(),
|
|
42
|
+
halfDay: zod.z.boolean().optional()
|
|
43
|
+
});
|
|
44
|
+
var ResolveDayInputSchema = zod.z.object({
|
|
45
|
+
date: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Duty date in worksite local wall-clock, YYYY-MM-DD."),
|
|
46
|
+
punches: zod.z.array(PunchSchema),
|
|
47
|
+
shift: ShiftConfigSchema,
|
|
48
|
+
policy: AttendancePolicySchema.optional(),
|
|
49
|
+
leave: LeaveDaySchema.nullable().optional(),
|
|
50
|
+
holiday: zod.z.boolean().optional(),
|
|
51
|
+
weekend: zod.z.boolean().optional()
|
|
52
|
+
});
|
|
53
|
+
var RosterPatternSchema = zod.z.union([
|
|
54
|
+
zod.z.enum(["2-2-3", "4-on-4-off", "dupont", "pitman"]),
|
|
55
|
+
zod.z.object({
|
|
56
|
+
custom: zod.z.array(
|
|
57
|
+
zod.z.union([
|
|
58
|
+
zod.z.literal("off"),
|
|
59
|
+
zod.z.object({
|
|
60
|
+
label: zod.z.string(),
|
|
61
|
+
start: zod.z.string().regex(/^\d{2}:\d{2}$/),
|
|
62
|
+
end: zod.z.string().regex(/^\d{2}:\d{2}$/)
|
|
63
|
+
})
|
|
64
|
+
])
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
]);
|
|
68
|
+
var RoundingOptionsSchema = zod.z.object({
|
|
69
|
+
unit: zod.z.number().int().min(1),
|
|
70
|
+
mode: zod.z.enum(["nearest", "up", "down"]).optional(),
|
|
71
|
+
applyTo: zod.z.array(zod.z.enum(["workedMinutes", "otMinutes", "lateByMinutes", "earlyOutMinutes", "breaksDeducted"])).optional()
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// src/util.ts
|
|
75
|
+
function jsonText(value) {
|
|
76
|
+
return { type: "text", text: JSON.stringify(value, null, 2) };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/tools/resolve-day.ts
|
|
80
|
+
function registerResolveDay(server) {
|
|
81
|
+
server.tool(
|
|
82
|
+
"resolve_day",
|
|
83
|
+
"Resolve a single duty day from raw clock punches and a shift definition. Returns a structured DayResult with status, worked minutes, lateness, early-out, overtime, overnight handling, breaks-deducted minutes, data-integrity flags, and the resolved in/out segments.",
|
|
84
|
+
ResolveDayInputSchema.shape,
|
|
85
|
+
async (input) => {
|
|
86
|
+
const result = core.resolveDay(input);
|
|
87
|
+
return { content: [jsonText(result)] };
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
function registerResolvePeriod(server) {
|
|
92
|
+
server.tool(
|
|
93
|
+
"resolve_period",
|
|
94
|
+
"Resolve a sequence of duty days (a week, a pay period, a month). Returns per-day DayResult entries; optionally include an aggregated PeriodSummary with attendance rate and flag counts.",
|
|
95
|
+
{
|
|
96
|
+
days: zod.z.array(ResolveDayInputSchema).describe("Inputs for each duty day in the period."),
|
|
97
|
+
summary: zod.z.boolean().optional().describe("Include the aggregated PeriodSummary. Default: true.")
|
|
98
|
+
},
|
|
99
|
+
async ({ days, summary }) => {
|
|
100
|
+
const results = core.resolveRange(days);
|
|
101
|
+
const payload = summary === false ? { days: results } : { days: results, summary: core.summarize(results) };
|
|
102
|
+
return { content: [jsonText(payload)] };
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
function registerEvaluateBreakCompliance(server) {
|
|
107
|
+
server.tool(
|
|
108
|
+
"evaluate_break_compliance",
|
|
109
|
+
"Analyse meal & rest period compliance for a duty day under a jurisdiction rule pack. v0.1 ships the California pack (Labor Code \xA7\xA7 226.7, 512; IWC wage orders). Returns per-meal/rest analysis, premium hours owed at the regular rate, waiver issues, and rebuttable-presumption risk per Donohue v. AMN.",
|
|
110
|
+
{
|
|
111
|
+
input: ResolveDayInputSchema.describe("Raw inputs for the day under review."),
|
|
112
|
+
jurisdiction: zod.z.enum(["CA"]).describe("Bundled jurisdiction rule pack to apply. Currently CA only; more arrive in subsequent minor versions."),
|
|
113
|
+
waivers: zod.z.array(
|
|
114
|
+
zod.z.object({
|
|
115
|
+
applies: zod.z.enum(["first-meal", "second-meal"]),
|
|
116
|
+
date: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
117
|
+
signed: zod.z.boolean(),
|
|
118
|
+
fileRef: zod.z.string().optional()
|
|
119
|
+
})
|
|
120
|
+
).optional()
|
|
121
|
+
},
|
|
122
|
+
async ({ input, jurisdiction, waivers }) => {
|
|
123
|
+
const result = core.resolveDay(input);
|
|
124
|
+
const compliance = core.evaluateBreakCompliance({
|
|
125
|
+
result,
|
|
126
|
+
rules: core.BREAK_RULE_SETS[jurisdiction],
|
|
127
|
+
waivers: waivers ?? []
|
|
128
|
+
});
|
|
129
|
+
return { content: [jsonText({ result, compliance })] };
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
function registerApplyRounding(server) {
|
|
134
|
+
server.tool(
|
|
135
|
+
"apply_rounding",
|
|
136
|
+
"Produce a rounded view of a resolved day's worked & overtime minutes without losing the exact-minute result. Useful for the California-style 'exact-minute is the baseline; rounding must be provably neutral' pattern \u2014 keep both views and compare across populations.",
|
|
137
|
+
{
|
|
138
|
+
input: ResolveDayInputSchema,
|
|
139
|
+
rounding: RoundingOptionsSchema
|
|
140
|
+
},
|
|
141
|
+
async ({ input, rounding }) => {
|
|
142
|
+
const exact = core.resolveDay(input);
|
|
143
|
+
const rounded = core.applyRounding(exact, rounding);
|
|
144
|
+
return { content: [jsonText({ exact, rounded })] };
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
function registerGenerateRoster(server) {
|
|
149
|
+
server.tool(
|
|
150
|
+
"generate_roster",
|
|
151
|
+
"Generate a rotating roster from a built-in pattern ('2-2-3', '4-on-4-off', 'dupont', 'pitman') or a custom day cycle. Returns one assignment per calendar date with shift label and HH:MM window, or null on rest days.",
|
|
152
|
+
{
|
|
153
|
+
pattern: RosterPatternSchema,
|
|
154
|
+
startDate: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
155
|
+
days: zod.z.number().int().min(0)
|
|
156
|
+
},
|
|
157
|
+
async ({ pattern, startDate, days }) => {
|
|
158
|
+
const roster = core.generateRoster(pattern, startDate, days);
|
|
159
|
+
return { content: [jsonText(roster)] };
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
function registerListRulePacks(server) {
|
|
164
|
+
server.tool(
|
|
165
|
+
"list_rule_packs",
|
|
166
|
+
"List bundled jurisdiction rule packs (meal/rest compliance). Returns each pack with id, human label, citation source, and full rule definitions.",
|
|
167
|
+
{},
|
|
168
|
+
async () => {
|
|
169
|
+
const packs = Object.values(core.BREAK_RULE_SETS).map((pack) => ({
|
|
170
|
+
id: pack.id,
|
|
171
|
+
label: pack.label,
|
|
172
|
+
source: pack.source,
|
|
173
|
+
rules: { meal: pack.meal, rest: pack.rest }
|
|
174
|
+
}));
|
|
175
|
+
return { content: [jsonText({ packs })] };
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
function registerAuditPeriodCompliance(server) {
|
|
180
|
+
server.tool(
|
|
181
|
+
"audit_period_compliance",
|
|
182
|
+
"Run a wage-and-hour compliance audit across multiple days. For each day, resolves attendance and evaluates meal/rest compliance under the chosen jurisdiction rule pack. Returns per-day breakdown plus period totals: hours of premium owed (meal + rest), days at risk, days with rebuttable-presumption exposure, and a flag-count heatmap. Use this for monthly payroll review, pre-audit triage, or a manager dashboard.",
|
|
183
|
+
{
|
|
184
|
+
employeeId: zod.z.string().optional().describe("Optional employee identifier. Echoed in the report for downstream routing."),
|
|
185
|
+
jurisdiction: zod.z.enum(["CA"]).describe("Jurisdiction rule pack. Currently CA only; more arrive in subsequent minor versions."),
|
|
186
|
+
days: zod.z.array(ResolveDayInputSchema).describe("One ResolveDayInput per duty date in the audit window. Order is preserved."),
|
|
187
|
+
waivers: zod.z.array(
|
|
188
|
+
zod.z.object({
|
|
189
|
+
applies: zod.z.enum(["first-meal", "second-meal"]),
|
|
190
|
+
date: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
191
|
+
signed: zod.z.boolean(),
|
|
192
|
+
fileRef: zod.z.string().optional()
|
|
193
|
+
})
|
|
194
|
+
).optional()
|
|
195
|
+
},
|
|
196
|
+
async ({ employeeId, jurisdiction, days, waivers = [] }) => {
|
|
197
|
+
const rules = core.BREAK_RULE_SETS[jurisdiction];
|
|
198
|
+
const perDay = days.map((input) => {
|
|
199
|
+
const result = core.resolveDay(input);
|
|
200
|
+
const compliance = core.evaluateBreakCompliance({ result, rules, waivers });
|
|
201
|
+
return { date: input.date, result, compliance };
|
|
202
|
+
});
|
|
203
|
+
const totalPremiumHours = perDay.reduce(
|
|
204
|
+
(acc, d) => ({
|
|
205
|
+
meal: acc.meal + d.compliance.premiumsOwed.meal,
|
|
206
|
+
rest: acc.rest + d.compliance.premiumsOwed.rest
|
|
207
|
+
}),
|
|
208
|
+
{ meal: 0, rest: 0 }
|
|
209
|
+
);
|
|
210
|
+
const daysByStatus = perDay.reduce((acc, d) => {
|
|
211
|
+
acc[d.result.status] = (acc[d.result.status] ?? 0) + 1;
|
|
212
|
+
return acc;
|
|
213
|
+
}, {});
|
|
214
|
+
const flagCounts = perDay.reduce((acc, d) => {
|
|
215
|
+
for (const f of d.result.flags) acc[f] = (acc[f] ?? 0) + 1;
|
|
216
|
+
return acc;
|
|
217
|
+
}, {});
|
|
218
|
+
const highRiskDays = perDay.filter((d) => d.compliance.presumptionRisk === "high").map((d) => d.date);
|
|
219
|
+
const daysAtRisk = perDay.filter((d) => d.compliance.presumptionRisk !== "low").length;
|
|
220
|
+
const presumptionRiskCounts = perDay.reduce((acc, d) => {
|
|
221
|
+
acc[d.compliance.presumptionRisk] = (acc[d.compliance.presumptionRisk] ?? 0) + 1;
|
|
222
|
+
return acc;
|
|
223
|
+
}, {});
|
|
224
|
+
const waiverIssuesAll = perDay.flatMap(
|
|
225
|
+
(d) => d.compliance.waiverIssues.map((issue) => ({ date: d.date, issue }))
|
|
226
|
+
);
|
|
227
|
+
const totalWorkedMinutes = perDay.reduce((acc, d) => acc + d.result.workedMinutes, 0);
|
|
228
|
+
const totalOtMinutes = perDay.reduce((acc, d) => acc + d.result.otMinutes, 0);
|
|
229
|
+
return {
|
|
230
|
+
content: [
|
|
231
|
+
jsonText({
|
|
232
|
+
employeeId: employeeId ?? null,
|
|
233
|
+
jurisdiction,
|
|
234
|
+
daysAnalysed: days.length,
|
|
235
|
+
workedHours: +(totalWorkedMinutes / 60).toFixed(2),
|
|
236
|
+
overtimeHours: +(totalOtMinutes / 60).toFixed(2),
|
|
237
|
+
totalPremiumHours,
|
|
238
|
+
daysAtRisk,
|
|
239
|
+
highRiskDays,
|
|
240
|
+
presumptionRiskCounts,
|
|
241
|
+
daysByStatus,
|
|
242
|
+
flagCounts,
|
|
243
|
+
waiverIssues: waiverIssuesAll,
|
|
244
|
+
perDay
|
|
245
|
+
})
|
|
246
|
+
]
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
function registerDiagnosePunches(server) {
|
|
252
|
+
server.tool(
|
|
253
|
+
"diagnose_punches",
|
|
254
|
+
"Triage raw clock punches before trusting them: count, sort, dedup, surface duplicates / odd-punch counts / round-number bias, report longest and shortest gaps, and (when an expected shift is provided) flag punches that fall outside it. Returns a recommendation: 'usable', 'review', or 'reject'.",
|
|
255
|
+
{
|
|
256
|
+
date: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Duty date for the punches, YYYY-MM-DD."),
|
|
257
|
+
punches: zod.z.array(PunchSchema),
|
|
258
|
+
expectedShift: ShiftConfigSchema.optional().describe(
|
|
259
|
+
"Optional shift definition. When provided, the report flags punches outside [start - 4h, end + 4h] as off-shift."
|
|
260
|
+
),
|
|
261
|
+
dedupeSeconds: zod.z.number().int().min(0).optional()
|
|
262
|
+
},
|
|
263
|
+
async ({ date, punches, expectedShift, dedupeSeconds }) => {
|
|
264
|
+
const shift = expectedShift ?? { start: "00:00", end: "23:59" };
|
|
265
|
+
const result = core.resolveDay({
|
|
266
|
+
date,
|
|
267
|
+
punches,
|
|
268
|
+
shift,
|
|
269
|
+
policy: { pairing: "in-out-pairs", dedupeSeconds: dedupeSeconds ?? 60 }
|
|
270
|
+
});
|
|
271
|
+
const flagSet = new Set(result.flags);
|
|
272
|
+
const segs = result.segments;
|
|
273
|
+
const gaps = [];
|
|
274
|
+
for (let i = 0; i < segs.length - 1; i += 1) {
|
|
275
|
+
const a = segs[i].out;
|
|
276
|
+
const b = segs[i + 1].in;
|
|
277
|
+
const minutes = Math.round((Date.parse(b) - Date.parse(a)) / 6e4);
|
|
278
|
+
if (minutes > 0) gaps.push(minutes);
|
|
279
|
+
}
|
|
280
|
+
const longestGapMinutes = gaps.length === 0 ? 0 : Math.max(...gaps);
|
|
281
|
+
const shortestGapMinutes = gaps.length === 0 ? 0 : Math.min(...gaps);
|
|
282
|
+
const issues = [];
|
|
283
|
+
if (flagSet.has("duplicate-punch")) issues.push("Duplicate punches were dropped \u2014 review your device dedupe window.");
|
|
284
|
+
if (flagSet.has("odd-punch-count")) issues.push("Odd punch count \u2014 at least one in or out is missing.");
|
|
285
|
+
if (flagSet.has("round-number-bias")) issues.push("Every punch lands on a 5-minute boundary with zero seconds \u2014 likely manual entry, not a device read.");
|
|
286
|
+
if (flagSet.has("no-punches")) issues.push("No usable punches.");
|
|
287
|
+
if (flagSet.has("missing-out-resolved")) issues.push("Engine synthesised a punch-out at shift end to recover \u2014 original record is incomplete.");
|
|
288
|
+
const recommendation = flagSet.has("no-punches") || flagSet.has("odd-punch-count") ? "reject" : flagSet.has("round-number-bias") || flagSet.has("duplicate-punch") ? "review" : "usable";
|
|
289
|
+
return {
|
|
290
|
+
content: [
|
|
291
|
+
jsonText({
|
|
292
|
+
date,
|
|
293
|
+
count: punches.length,
|
|
294
|
+
firstIn: result.firstIn,
|
|
295
|
+
lastOut: result.lastOut,
|
|
296
|
+
segments: result.segments.length,
|
|
297
|
+
longestGapMinutes,
|
|
298
|
+
shortestGapMinutes,
|
|
299
|
+
flags: result.flags,
|
|
300
|
+
issues,
|
|
301
|
+
recommendation,
|
|
302
|
+
workedMinutesEstimate: result.workedMinutes,
|
|
303
|
+
spansMidnight: result.spansMidnight
|
|
304
|
+
})
|
|
305
|
+
]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
var OVERVIEW_MARKDOWN = `# attendance-engine
|
|
311
|
+
|
|
312
|
+
Pure-function workforce attendance resolver. Punches in \u2192 resolved day out: status,
|
|
313
|
+
worked minutes, lateness, early-out, overtime, overnight handling, segments, and
|
|
314
|
+
data-integrity flags. Zero runtime dependencies, framework-agnostic, time-zone-safe.
|
|
315
|
+
|
|
316
|
+
This MCP server exposes the core engine as tools your AI agent can call:
|
|
317
|
+
|
|
318
|
+
- **resolve_day** \u2014 one duty day
|
|
319
|
+
- **resolve_period** \u2014 a pay period, with optional summary
|
|
320
|
+
- **evaluate_break_compliance** \u2014 California meal/rest analysis (more packs coming)
|
|
321
|
+
- **apply_rounding** \u2014 produce a rounded view alongside the exact one
|
|
322
|
+
- **generate_roster** \u2014 rotating-roster generation (2-2-3, 4-on-4-off, dupont, pitman, custom)
|
|
323
|
+
- **list_rule_packs** \u2014 discover available jurisdictions
|
|
324
|
+
|
|
325
|
+
Time-zone rule: every ISO timestamp must carry an explicit offset. The engine never
|
|
326
|
+
reads the host timezone. DST days work because offsets are explicit.
|
|
327
|
+
|
|
328
|
+
Source: https://github.com/arifur9993/attendance-engine
|
|
329
|
+
`;
|
|
330
|
+
var API_MARKDOWN = `# attendance-engine \u2014 quick API reference
|
|
331
|
+
|
|
332
|
+
## resolve_day(input)
|
|
333
|
+
|
|
334
|
+
\`\`\`
|
|
335
|
+
input = {
|
|
336
|
+
date: 'YYYY-MM-DD',
|
|
337
|
+
punches: [{ at: 'ISO with offset', source?, location? }, ...],
|
|
338
|
+
shift: { start: 'HH:MM', end: 'HH:MM', breaks?, graceIn?, graceOut?, minHalfDayMinutes?, flexible? },
|
|
339
|
+
policy?: { pairing?, treatMissingOutAs?, midnightCutover?, lateAfterGrace?,
|
|
340
|
+
otThresholdMinutes?, otRoundingUnit?, otMode?, standardDayMinutes?,
|
|
341
|
+
dedupeSeconds?, tzOffsetMinutes? },
|
|
342
|
+
leave?, holiday?, weekend?
|
|
343
|
+
}
|
|
344
|
+
\`\`\`
|
|
345
|
+
|
|
346
|
+
Returns \`DayResult\`: status, firstIn, lastOut, workedMinutes, lateByMinutes,
|
|
347
|
+
earlyOutMinutes, otMinutes, spansMidnight, breaksDeducted, flags, segments.
|
|
348
|
+
|
|
349
|
+
## evaluate_break_compliance({ input, jurisdiction, waivers? })
|
|
350
|
+
|
|
351
|
+
Returns per-meal and per-rest analysis with hour-premiums owed (capped 1 + 1 per day
|
|
352
|
+
under CA) and presumption risk per Donohue v. AMN. Waivers can be \`first-meal\` (valid
|
|
353
|
+
on shifts \u2264 6h) or \`second-meal\` (\u2264 12h, only if first not waived).
|
|
354
|
+
|
|
355
|
+
## More
|
|
356
|
+
|
|
357
|
+
Full docs: https://github.com/arifur9993/attendance-engine/blob/main/packages/core/docs/api.md
|
|
358
|
+
`;
|
|
359
|
+
function registerDocsResources(server) {
|
|
360
|
+
server.resource(
|
|
361
|
+
"overview",
|
|
362
|
+
"attendance://docs/overview",
|
|
363
|
+
{
|
|
364
|
+
title: "attendance-engine \u2014 overview",
|
|
365
|
+
description: "Project overview, time-zone rules, list of available tools.",
|
|
366
|
+
mimeType: "text/markdown"
|
|
367
|
+
},
|
|
368
|
+
async (uri) => ({
|
|
369
|
+
contents: [
|
|
370
|
+
{
|
|
371
|
+
uri: uri.href,
|
|
372
|
+
mimeType: "text/markdown",
|
|
373
|
+
text: OVERVIEW_MARKDOWN
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
})
|
|
377
|
+
);
|
|
378
|
+
server.resource(
|
|
379
|
+
"api",
|
|
380
|
+
"attendance://docs/api",
|
|
381
|
+
{
|
|
382
|
+
title: "attendance-engine \u2014 API quick reference",
|
|
383
|
+
description: "Compact reference for the tools exposed by this MCP server.",
|
|
384
|
+
mimeType: "text/markdown"
|
|
385
|
+
},
|
|
386
|
+
async (uri) => ({
|
|
387
|
+
contents: [
|
|
388
|
+
{
|
|
389
|
+
uri: uri.href,
|
|
390
|
+
mimeType: "text/markdown",
|
|
391
|
+
text: API_MARKDOWN
|
|
392
|
+
}
|
|
393
|
+
]
|
|
394
|
+
})
|
|
395
|
+
);
|
|
396
|
+
server.resource(
|
|
397
|
+
"rules-ca",
|
|
398
|
+
"attendance://rules/CA",
|
|
399
|
+
{
|
|
400
|
+
title: "California meal & rest rule pack",
|
|
401
|
+
description: "The California (Labor Code \xA7\xA7 226.7, 512; IWC wage orders) meal/rest rule pack used by evaluate_break_compliance.",
|
|
402
|
+
mimeType: "application/json"
|
|
403
|
+
},
|
|
404
|
+
async (uri) => ({
|
|
405
|
+
contents: [
|
|
406
|
+
{
|
|
407
|
+
uri: uri.href,
|
|
408
|
+
mimeType: "application/json",
|
|
409
|
+
text: JSON.stringify(core.CA_BREAK_RULES, null, 2)
|
|
410
|
+
}
|
|
411
|
+
]
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
var TEMPLATE = `You are an attendance/payroll analyst with access to the \`attendance-engine\` MCP tools (resolve_day, evaluate_break_compliance, apply_rounding, \u2026). Use them \u2014 don't reason about timestamps in your head.
|
|
416
|
+
|
|
417
|
+
Task: analyse the following timecard.
|
|
418
|
+
|
|
419
|
+
- Date: {date}
|
|
420
|
+
- Jurisdiction: {jurisdiction}
|
|
421
|
+
- Shift: {shift}
|
|
422
|
+
- Punches: {punches}
|
|
423
|
+
|
|
424
|
+
Steps to follow:
|
|
425
|
+
1. Call \`resolve_day\` with the inputs above.
|
|
426
|
+
2. Call \`evaluate_break_compliance\` with the same inputs and jurisdiction.
|
|
427
|
+
3. Summarise in plain English: was this person on time? Did they get their meal/rest periods? Is any premium owed? Are there any data-integrity flags (duplicate punches, odd punch count, round-number bias)?
|
|
428
|
+
4. If anything is ambiguous (e.g. single-segment day with no meal events), say so \u2014 don't guess.`;
|
|
429
|
+
function registerAnalyseTimecardPrompt(server) {
|
|
430
|
+
server.prompt(
|
|
431
|
+
"analyse_timecard",
|
|
432
|
+
"Walk a model through analysing a single duty day for attendance + compliance using the available tools. Outputs an instruction prompt the model can follow.",
|
|
433
|
+
{
|
|
434
|
+
date: zod.z.string().describe("Duty date, YYYY-MM-DD."),
|
|
435
|
+
jurisdiction: zod.z.string().describe('Jurisdiction rule pack id (e.g. "CA"). Use "none" to skip compliance.'),
|
|
436
|
+
shift: zod.z.string().describe('Shift description, e.g. "09:00\u201318:00 with 10-minute grace and an unpaid 13:00\u201314:00 lunch".'),
|
|
437
|
+
punches: zod.z.string().describe('Comma-separated ISO punches, e.g. "2026-06-01T08:57:00+06:00, 2026-06-01T18:04:00+06:00".')
|
|
438
|
+
},
|
|
439
|
+
async (args) => {
|
|
440
|
+
const text = TEMPLATE.replace("{date}", args.date).replace("{jurisdiction}", args.jurisdiction).replace("{shift}", args.shift).replace("{punches}", args.punches);
|
|
441
|
+
return {
|
|
442
|
+
messages: [{ role: "user", content: { type: "text", text } }]
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
function registerRosterPlannerPrompt(server) {
|
|
448
|
+
server.prompt(
|
|
449
|
+
"roster_planner",
|
|
450
|
+
"Walk a model through building a rotating roster using the generate_roster tool.",
|
|
451
|
+
{
|
|
452
|
+
pattern: zod.z.string().describe('Pattern name ("2-2-3", "4-on-4-off", "dupont", "pitman") or a description of a custom cycle.'),
|
|
453
|
+
startDate: zod.z.string().describe("Start date, YYYY-MM-DD."),
|
|
454
|
+
days: zod.z.string().describe('How many days to generate, e.g. "28".')
|
|
455
|
+
},
|
|
456
|
+
async (args) => ({
|
|
457
|
+
messages: [
|
|
458
|
+
{
|
|
459
|
+
role: "user",
|
|
460
|
+
content: {
|
|
461
|
+
type: "text",
|
|
462
|
+
text: `You have access to the \`attendance-engine\` MCP tools. Build a ${args.pattern} roster starting ${args.startDate} for ${args.days} days by calling \`generate_roster\`. Then format the result as a Markdown table with columns: Date, Day of week, Shift label, Start, End. Highlight rest days.`
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
]
|
|
466
|
+
})
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/server.ts
|
|
471
|
+
function createServer(options = {}) {
|
|
472
|
+
const server = new mcp_js.McpServer({
|
|
473
|
+
name: options.name ?? "attendance-engine",
|
|
474
|
+
version: options.version ?? "0.1.0"
|
|
475
|
+
});
|
|
476
|
+
registerResolveDay(server);
|
|
477
|
+
registerResolvePeriod(server);
|
|
478
|
+
registerEvaluateBreakCompliance(server);
|
|
479
|
+
registerApplyRounding(server);
|
|
480
|
+
registerGenerateRoster(server);
|
|
481
|
+
registerListRulePacks(server);
|
|
482
|
+
registerAuditPeriodCompliance(server);
|
|
483
|
+
registerDiagnosePunches(server);
|
|
484
|
+
registerDocsResources(server);
|
|
485
|
+
registerAnalyseTimecardPrompt(server);
|
|
486
|
+
registerRosterPlannerPrompt(server);
|
|
487
|
+
return server;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// src/cli.ts
|
|
491
|
+
async function main() {
|
|
492
|
+
const server = createServer();
|
|
493
|
+
const transport = new stdio_js.StdioServerTransport();
|
|
494
|
+
await server.connect(transport);
|
|
495
|
+
}
|
|
496
|
+
main().catch((err) => {
|
|
497
|
+
console.error("[attendance-engine-mcp] fatal:", err);
|
|
498
|
+
process.exit(1);
|
|
499
|
+
});
|
|
500
|
+
//# sourceMappingURL=cli.cjs.map
|
|
501
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schemas.ts","../src/util.ts","../src/tools/resolve-day.ts","../src/tools/resolve-period.ts","../src/tools/evaluate-break-compliance.ts","../src/tools/apply-rounding.ts","../src/tools/generate-roster.ts","../src/tools/list-rule-packs.ts","../src/tools/audit-period-compliance.ts","../src/tools/diagnose-punches.ts","../src/resources/docs.ts","../src/prompts/analyse-timecard.ts","../src/prompts/roster-planner.ts","../src/server.ts","../src/cli.ts"],"names":["z","resolveDay","resolveRange","summarize","evaluateBreakCompliance","BREAK_RULE_SETS","applyRounding","generateRoster","CA_BREAK_RULES","McpServer","StdioServerTransport"],"mappings":"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","file":"cli.cjs","sourcesContent":["/**\n * Zod schemas mirroring the public types of @attendance-engine/core.\n *\n * These are kept in sync with the engine's TypeScript types but live here so the MCP\n * server can produce clean JSON-schema descriptions for tool clients. When the engine\n * adds a field, mirror it here.\n */\n\nimport { z } from 'zod';\n\nexport const PunchSchema = z.object({\n at: z\n .string()\n .describe('ISO-8601 instant with an explicit offset, e.g. \"2026-06-01T08:57:00+06:00\".'),\n source: z.enum(['biometric', 'mobile', 'manual', 'web']).optional(),\n location: z.string().optional(),\n});\n\nexport const BreakWindowSchema = z.object({\n start: z.string().regex(/^\\d{2}:\\d{2}$/),\n end: z.string().regex(/^\\d{2}:\\d{2}$/),\n paid: z.boolean(),\n});\n\nexport const ShiftConfigSchema = z.object({\n start: z\n .string()\n .regex(/^\\d{2}:\\d{2}$/)\n .describe('Scheduled start, HH:MM, worksite local wall-clock.'),\n end: z\n .string()\n .regex(/^\\d{2}:\\d{2}$/)\n .describe('Scheduled end, HH:MM. If <= start, the shift is overnight and ends on the next day.'),\n breaks: z.array(BreakWindowSchema).optional(),\n graceIn: z.number().int().min(0).optional(),\n graceOut: z.number().int().min(0).optional(),\n minHalfDayMinutes: z.number().int().min(0).optional(),\n flexible: z.boolean().optional(),\n});\n\nexport const AttendancePolicySchema = z.object({\n pairing: z.enum(['first-last', 'in-out-pairs']).optional(),\n treatMissingOutAs: z.enum(['absent', 'shift-end', 'half-day', 'flag-only']).optional(),\n midnightCutover: z.enum(['shift-anchored', 'calendar-day']).optional(),\n lateAfterGrace: z.enum(['mark-late', 'deduct', 'ignore']).optional(),\n otThresholdMinutes: z.number().int().min(0).optional(),\n otRoundingUnit: z.number().int().min(1).optional(),\n otMode: z.enum(['shift-based', 'fixed-hours', 'daily-cap']).optional(),\n standardDayMinutes: z.number().int().min(0).optional(),\n dedupeSeconds: z.number().int().min(0).optional(),\n tzOffsetMinutes: z.number().int().optional(),\n});\n\nexport const LeaveDaySchema = z.object({\n type: z.string().optional(),\n halfDay: z.boolean().optional(),\n});\n\nexport const ResolveDayInputSchema = z.object({\n date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .describe('Duty date in worksite local wall-clock, YYYY-MM-DD.'),\n punches: z.array(PunchSchema),\n shift: ShiftConfigSchema,\n policy: AttendancePolicySchema.optional(),\n leave: LeaveDaySchema.nullable().optional(),\n holiday: z.boolean().optional(),\n weekend: z.boolean().optional(),\n});\n\nexport const RosterPatternSchema = z.union([\n z.enum(['2-2-3', '4-on-4-off', 'dupont', 'pitman']),\n z.object({\n custom: z.array(\n z.union([\n z.literal('off'),\n z.object({\n label: z.string(),\n start: z.string().regex(/^\\d{2}:\\d{2}$/),\n end: z.string().regex(/^\\d{2}:\\d{2}$/),\n }),\n ]),\n ),\n }),\n]);\n\nexport const RoundingOptionsSchema = z.object({\n unit: z.number().int().min(1),\n mode: z.enum(['nearest', 'up', 'down']).optional(),\n applyTo: z\n .array(z.enum(['workedMinutes', 'otMinutes', 'lateByMinutes', 'earlyOutMinutes', 'breaksDeducted']))\n .optional(),\n});\n","/** Tiny helpers shared across tool handlers. */\n\n/**\n * Serialize a value as pretty JSON and wrap it as an MCP text-content block.\n * Centralised so every tool's response shape stays consistent.\n */\nexport function jsonText(value: unknown): { type: 'text'; text: string } {\n return { type: 'text', text: JSON.stringify(value, null, 2) };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { resolveDay } from '@attendance-engine/core';\nimport { ResolveDayInputSchema } from '../schemas.js';\nimport { jsonText } from '../util.js';\n\nexport function registerResolveDay(server: McpServer): void {\n server.tool(\n 'resolve_day',\n \"Resolve a single duty day from raw clock punches and a shift definition. Returns a structured DayResult with status, worked minutes, lateness, early-out, overtime, overnight handling, breaks-deducted minutes, data-integrity flags, and the resolved in/out segments.\",\n ResolveDayInputSchema.shape,\n async (input) => {\n const result = resolveDay(input);\n return { content: [jsonText(result)] };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { resolveRange, summarize } from '@attendance-engine/core';\nimport { z } from 'zod';\nimport { ResolveDayInputSchema } from '../schemas.js';\nimport { jsonText } from '../util.js';\n\nexport function registerResolvePeriod(server: McpServer): void {\n server.tool(\n 'resolve_period',\n \"Resolve a sequence of duty days (a week, a pay period, a month). Returns per-day DayResult entries; optionally include an aggregated PeriodSummary with attendance rate and flag counts.\",\n {\n days: z.array(ResolveDayInputSchema).describe('Inputs for each duty day in the period.'),\n summary: z.boolean().optional().describe('Include the aggregated PeriodSummary. Default: true.'),\n },\n async ({ days, summary }) => {\n const results = resolveRange(days);\n const payload = summary === false\n ? { days: results }\n : { days: results, summary: summarize(results) };\n return { content: [jsonText(payload)] };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n resolveDay,\n evaluateBreakCompliance,\n BREAK_RULE_SETS,\n} from '@attendance-engine/core';\nimport { z } from 'zod';\nimport { ResolveDayInputSchema } from '../schemas.js';\nimport { jsonText } from '../util.js';\n\nexport function registerEvaluateBreakCompliance(server: McpServer): void {\n server.tool(\n 'evaluate_break_compliance',\n \"Analyse meal & rest period compliance for a duty day under a jurisdiction rule pack. v0.1 ships the California pack (Labor Code §§ 226.7, 512; IWC wage orders). Returns per-meal/rest analysis, premium hours owed at the regular rate, waiver issues, and rebuttable-presumption risk per Donohue v. AMN.\",\n {\n input: ResolveDayInputSchema.describe('Raw inputs for the day under review.'),\n jurisdiction: z\n .enum(['CA'])\n .describe('Bundled jurisdiction rule pack to apply. Currently CA only; more arrive in subsequent minor versions.'),\n waivers: z\n .array(\n z.object({\n applies: z.enum(['first-meal', 'second-meal']),\n date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional(),\n signed: z.boolean(),\n fileRef: z.string().optional(),\n }),\n )\n .optional(),\n },\n async ({ input, jurisdiction, waivers }) => {\n const result = resolveDay(input);\n const compliance = evaluateBreakCompliance({\n result,\n rules: BREAK_RULE_SETS[jurisdiction],\n waivers: waivers ?? [],\n });\n return { content: [jsonText({ result, compliance })] };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { resolveDay, applyRounding } from '@attendance-engine/core';\nimport { ResolveDayInputSchema, RoundingOptionsSchema } from '../schemas.js';\nimport { jsonText } from '../util.js';\n\nexport function registerApplyRounding(server: McpServer): void {\n server.tool(\n 'apply_rounding',\n \"Produce a rounded view of a resolved day's worked & overtime minutes without losing the exact-minute result. Useful for the California-style 'exact-minute is the baseline; rounding must be provably neutral' pattern — keep both views and compare across populations.\",\n {\n input: ResolveDayInputSchema,\n rounding: RoundingOptionsSchema,\n },\n async ({ input, rounding }) => {\n const exact = resolveDay(input);\n const rounded = applyRounding(exact, rounding);\n return { content: [jsonText({ exact, rounded })] };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { generateRoster } from '@attendance-engine/core';\nimport { z } from 'zod';\nimport { RosterPatternSchema } from '../schemas.js';\nimport { jsonText } from '../util.js';\n\nexport function registerGenerateRoster(server: McpServer): void {\n server.tool(\n 'generate_roster',\n \"Generate a rotating roster from a built-in pattern ('2-2-3', '4-on-4-off', 'dupont', 'pitman') or a custom day cycle. Returns one assignment per calendar date with shift label and HH:MM window, or null on rest days.\",\n {\n pattern: RosterPatternSchema,\n startDate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n days: z.number().int().min(0),\n },\n async ({ pattern, startDate, days }) => {\n const roster = generateRoster(pattern, startDate, days);\n return { content: [jsonText(roster)] };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { BREAK_RULE_SETS } from '@attendance-engine/core';\nimport { jsonText } from '../util.js';\n\nexport function registerListRulePacks(server: McpServer): void {\n server.tool(\n 'list_rule_packs',\n 'List bundled jurisdiction rule packs (meal/rest compliance). Returns each pack with id, human label, citation source, and full rule definitions.',\n {},\n async () => {\n const packs = Object.values(BREAK_RULE_SETS).map((pack) => ({\n id: pack.id,\n label: pack.label,\n source: pack.source,\n rules: { meal: pack.meal, rest: pack.rest },\n }));\n return { content: [jsonText({ packs })] };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n resolveDay,\n evaluateBreakCompliance,\n BREAK_RULE_SETS,\n type DayResult,\n type BreakComplianceResult,\n} from '@attendance-engine/core';\nimport { z } from 'zod';\nimport { ResolveDayInputSchema } from '../schemas.js';\nimport { jsonText } from '../util.js';\n\n/**\n * The big one: run a multi-day wage-and-hour compliance audit and return a single\n * actionable report. This is what an HR / payroll / legal-ops agent actually wants —\n * \"tell me which days are at risk, total premiums owed, and who needs follow-up.\"\n */\nexport function registerAuditPeriodCompliance(server: McpServer): void {\n server.tool(\n 'audit_period_compliance',\n \"Run a wage-and-hour compliance audit across multiple days. For each day, resolves attendance and evaluates meal/rest compliance under the chosen jurisdiction rule pack. Returns per-day breakdown plus period totals: hours of premium owed (meal + rest), days at risk, days with rebuttable-presumption exposure, and a flag-count heatmap. Use this for monthly payroll review, pre-audit triage, or a manager dashboard.\",\n {\n employeeId: z\n .string()\n .optional()\n .describe('Optional employee identifier. Echoed in the report for downstream routing.'),\n jurisdiction: z\n .enum(['CA'])\n .describe('Jurisdiction rule pack. Currently CA only; more arrive in subsequent minor versions.'),\n days: z\n .array(ResolveDayInputSchema)\n .describe('One ResolveDayInput per duty date in the audit window. Order is preserved.'),\n waivers: z\n .array(\n z.object({\n applies: z.enum(['first-meal', 'second-meal']),\n date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional(),\n signed: z.boolean(),\n fileRef: z.string().optional(),\n }),\n )\n .optional(),\n },\n async ({ employeeId, jurisdiction, days, waivers = [] }) => {\n const rules = BREAK_RULE_SETS[jurisdiction];\n\n type PerDay = {\n date: string;\n result: DayResult;\n compliance: BreakComplianceResult;\n };\n\n const perDay: PerDay[] = days.map((input) => {\n const result = resolveDay(input);\n const compliance = evaluateBreakCompliance({ result, rules, waivers });\n return { date: input.date, result, compliance };\n });\n\n const totalPremiumHours = perDay.reduce(\n (acc, d) => ({\n meal: acc.meal + d.compliance.premiumsOwed.meal,\n rest: acc.rest + d.compliance.premiumsOwed.rest,\n }),\n { meal: 0, rest: 0 },\n );\n\n const daysByStatus = perDay.reduce<Record<string, number>>((acc, d) => {\n acc[d.result.status] = (acc[d.result.status] ?? 0) + 1;\n return acc;\n }, {});\n\n const flagCounts = perDay.reduce<Record<string, number>>((acc, d) => {\n for (const f of d.result.flags) acc[f] = (acc[f] ?? 0) + 1;\n return acc;\n }, {});\n\n const highRiskDays = perDay\n .filter((d) => d.compliance.presumptionRisk === 'high')\n .map((d) => d.date);\n\n const daysAtRisk = perDay.filter((d) => d.compliance.presumptionRisk !== 'low').length;\n\n const presumptionRiskCounts = perDay.reduce<Record<string, number>>((acc, d) => {\n acc[d.compliance.presumptionRisk] = (acc[d.compliance.presumptionRisk] ?? 0) + 1;\n return acc;\n }, {});\n\n const waiverIssuesAll = perDay.flatMap((d) =>\n d.compliance.waiverIssues.map((issue) => ({ date: d.date, issue })),\n );\n\n const totalWorkedMinutes = perDay.reduce((acc, d) => acc + d.result.workedMinutes, 0);\n const totalOtMinutes = perDay.reduce((acc, d) => acc + d.result.otMinutes, 0);\n\n return {\n content: [\n jsonText({\n employeeId: employeeId ?? null,\n jurisdiction,\n daysAnalysed: days.length,\n workedHours: +(totalWorkedMinutes / 60).toFixed(2),\n overtimeHours: +(totalOtMinutes / 60).toFixed(2),\n totalPremiumHours,\n daysAtRisk,\n highRiskDays,\n presumptionRiskCounts,\n daysByStatus,\n flagCounts,\n waiverIssues: waiverIssuesAll,\n perDay,\n }),\n ],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { resolveDay } from '@attendance-engine/core';\nimport { z } from 'zod';\nimport { PunchSchema, ShiftConfigSchema } from '../schemas.js';\nimport { jsonText } from '../util.js';\n\n/**\n * Triage helper. Takes raw punches and (optionally) a shift, returns a focused\n * report on data-integrity issues before you trust the timecard.\n *\n * Implementation reuses resolveDay against a wide synthetic shift so all of the\n * engine's flag detection runs without us re-implementing dedup / parsing.\n */\nexport function registerDiagnosePunches(server: McpServer): void {\n server.tool(\n 'diagnose_punches',\n \"Triage raw clock punches before trusting them: count, sort, dedup, surface duplicates / odd-punch counts / round-number bias, report longest and shortest gaps, and (when an expected shift is provided) flag punches that fall outside it. Returns a recommendation: 'usable', 'review', or 'reject'.\",\n {\n date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .describe('Duty date for the punches, YYYY-MM-DD.'),\n punches: z.array(PunchSchema),\n expectedShift: ShiftConfigSchema.optional().describe(\n 'Optional shift definition. When provided, the report flags punches outside [start - 4h, end + 4h] as off-shift.',\n ),\n dedupeSeconds: z.number().int().min(0).optional(),\n },\n async ({ date, punches, expectedShift, dedupeSeconds }) => {\n const shift = expectedShift ?? { start: '00:00', end: '23:59' };\n const result = resolveDay({\n date,\n punches,\n shift,\n policy: { pairing: 'in-out-pairs', dedupeSeconds: dedupeSeconds ?? 60 },\n });\n\n const flagSet = new Set(result.flags);\n\n // Gap analysis from the resolved segments + interstitial gaps.\n const segs = result.segments;\n const gaps: number[] = [];\n for (let i = 0; i < segs.length - 1; i += 1) {\n const a = segs[i]!.out;\n const b = segs[i + 1]!.in;\n const minutes = Math.round((Date.parse(b) - Date.parse(a)) / 60_000);\n if (minutes > 0) gaps.push(minutes);\n }\n\n const longestGapMinutes = gaps.length === 0 ? 0 : Math.max(...gaps);\n const shortestGapMinutes = gaps.length === 0 ? 0 : Math.min(...gaps);\n\n const issues: string[] = [];\n if (flagSet.has('duplicate-punch')) issues.push('Duplicate punches were dropped — review your device dedupe window.');\n if (flagSet.has('odd-punch-count')) issues.push('Odd punch count — at least one in or out is missing.');\n if (flagSet.has('round-number-bias')) issues.push('Every punch lands on a 5-minute boundary with zero seconds — likely manual entry, not a device read.');\n if (flagSet.has('no-punches')) issues.push('No usable punches.');\n if (flagSet.has('missing-out-resolved')) issues.push(\"Engine synthesised a punch-out at shift end to recover — original record is incomplete.\");\n\n const recommendation: 'usable' | 'review' | 'reject' =\n flagSet.has('no-punches') || flagSet.has('odd-punch-count')\n ? 'reject'\n : (flagSet.has('round-number-bias') || flagSet.has('duplicate-punch'))\n ? 'review'\n : 'usable';\n\n return {\n content: [\n jsonText({\n date,\n count: punches.length,\n firstIn: result.firstIn,\n lastOut: result.lastOut,\n segments: result.segments.length,\n longestGapMinutes,\n shortestGapMinutes,\n flags: result.flags,\n issues,\n recommendation,\n workedMinutesEstimate: result.workedMinutes,\n spansMidnight: result.spansMidnight,\n }),\n ],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { CA_BREAK_RULES } from '@attendance-engine/core';\n\nconst OVERVIEW_MARKDOWN = `# attendance-engine\n\nPure-function workforce attendance resolver. Punches in → resolved day out: status,\nworked minutes, lateness, early-out, overtime, overnight handling, segments, and\ndata-integrity flags. Zero runtime dependencies, framework-agnostic, time-zone-safe.\n\nThis MCP server exposes the core engine as tools your AI agent can call:\n\n- **resolve_day** — one duty day\n- **resolve_period** — a pay period, with optional summary\n- **evaluate_break_compliance** — California meal/rest analysis (more packs coming)\n- **apply_rounding** — produce a rounded view alongside the exact one\n- **generate_roster** — rotating-roster generation (2-2-3, 4-on-4-off, dupont, pitman, custom)\n- **list_rule_packs** — discover available jurisdictions\n\nTime-zone rule: every ISO timestamp must carry an explicit offset. The engine never\nreads the host timezone. DST days work because offsets are explicit.\n\nSource: https://github.com/arifur9993/attendance-engine\n`;\n\nconst API_MARKDOWN = `# attendance-engine — quick API reference\n\n## resolve_day(input)\n\n\\`\\`\\`\ninput = {\n date: 'YYYY-MM-DD',\n punches: [{ at: 'ISO with offset', source?, location? }, ...],\n shift: { start: 'HH:MM', end: 'HH:MM', breaks?, graceIn?, graceOut?, minHalfDayMinutes?, flexible? },\n policy?: { pairing?, treatMissingOutAs?, midnightCutover?, lateAfterGrace?,\n otThresholdMinutes?, otRoundingUnit?, otMode?, standardDayMinutes?,\n dedupeSeconds?, tzOffsetMinutes? },\n leave?, holiday?, weekend?\n}\n\\`\\`\\`\n\nReturns \\`DayResult\\`: status, firstIn, lastOut, workedMinutes, lateByMinutes,\nearlyOutMinutes, otMinutes, spansMidnight, breaksDeducted, flags, segments.\n\n## evaluate_break_compliance({ input, jurisdiction, waivers? })\n\nReturns per-meal and per-rest analysis with hour-premiums owed (capped 1 + 1 per day\nunder CA) and presumption risk per Donohue v. AMN. Waivers can be \\`first-meal\\` (valid\non shifts ≤ 6h) or \\`second-meal\\` (≤ 12h, only if first not waived).\n\n## More\n\nFull docs: https://github.com/arifur9993/attendance-engine/blob/main/packages/core/docs/api.md\n`;\n\nexport function registerDocsResources(server: McpServer): void {\n server.resource(\n 'overview',\n 'attendance://docs/overview',\n {\n title: 'attendance-engine — overview',\n description: 'Project overview, time-zone rules, list of available tools.',\n mimeType: 'text/markdown',\n },\n async (uri) => ({\n contents: [\n {\n uri: uri.href,\n mimeType: 'text/markdown',\n text: OVERVIEW_MARKDOWN,\n },\n ],\n }),\n );\n\n server.resource(\n 'api',\n 'attendance://docs/api',\n {\n title: 'attendance-engine — API quick reference',\n description: 'Compact reference for the tools exposed by this MCP server.',\n mimeType: 'text/markdown',\n },\n async (uri) => ({\n contents: [\n {\n uri: uri.href,\n mimeType: 'text/markdown',\n text: API_MARKDOWN,\n },\n ],\n }),\n );\n\n server.resource(\n 'rules-ca',\n 'attendance://rules/CA',\n {\n title: 'California meal & rest rule pack',\n description:\n 'The California (Labor Code §§ 226.7, 512; IWC wage orders) meal/rest rule pack used by evaluate_break_compliance.',\n mimeType: 'application/json',\n },\n async (uri) => ({\n contents: [\n {\n uri: uri.href,\n mimeType: 'application/json',\n text: JSON.stringify(CA_BREAK_RULES, null, 2),\n },\n ],\n }),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nconst TEMPLATE = `You are an attendance/payroll analyst with access to the \\\n\\`attendance-engine\\` MCP tools (resolve_day, evaluate_break_compliance, \\\napply_rounding, …). Use them — don't reason about timestamps in your head.\n\nTask: analyse the following timecard.\n\n- Date: {date}\n- Jurisdiction: {jurisdiction}\n- Shift: {shift}\n- Punches: {punches}\n\nSteps to follow:\n1. Call \\`resolve_day\\` with the inputs above.\n2. Call \\`evaluate_break_compliance\\` with the same inputs and jurisdiction.\n3. Summarise in plain English: was this person on time? Did they get their meal/rest \\\nperiods? Is any premium owed? Are there any data-integrity flags (duplicate punches, \\\nodd punch count, round-number bias)?\n4. If anything is ambiguous (e.g. single-segment day with no meal events), say so — \\\ndon't guess.`;\n\nexport function registerAnalyseTimecardPrompt(server: McpServer): void {\n server.prompt(\n 'analyse_timecard',\n 'Walk a model through analysing a single duty day for attendance + compliance using the available tools. Outputs an instruction prompt the model can follow.',\n {\n date: z.string().describe('Duty date, YYYY-MM-DD.'),\n jurisdiction: z.string().describe('Jurisdiction rule pack id (e.g. \"CA\"). Use \"none\" to skip compliance.'),\n shift: z.string().describe('Shift description, e.g. \"09:00–18:00 with 10-minute grace and an unpaid 13:00–14:00 lunch\".'),\n punches: z.string().describe('Comma-separated ISO punches, e.g. \"2026-06-01T08:57:00+06:00, 2026-06-01T18:04:00+06:00\".'),\n },\n async (args) => {\n const text = TEMPLATE\n .replace('{date}', args.date)\n .replace('{jurisdiction}', args.jurisdiction)\n .replace('{shift}', args.shift)\n .replace('{punches}', args.punches);\n return {\n messages: [{ role: 'user', content: { type: 'text', text } }],\n };\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nexport function registerRosterPlannerPrompt(server: McpServer): void {\n server.prompt(\n 'roster_planner',\n 'Walk a model through building a rotating roster using the generate_roster tool.',\n {\n pattern: z\n .string()\n .describe('Pattern name (\"2-2-3\", \"4-on-4-off\", \"dupont\", \"pitman\") or a description of a custom cycle.'),\n startDate: z.string().describe('Start date, YYYY-MM-DD.'),\n days: z.string().describe('How many days to generate, e.g. \"28\".'),\n },\n async (args) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `You have access to the \\`attendance-engine\\` MCP tools. Build a \\\n${args.pattern} roster starting ${args.startDate} for ${args.days} days by calling \\\n\\`generate_roster\\`. Then format the result as a Markdown table with columns: Date, \\\nDay of week, Shift label, Start, End. Highlight rest days.`,\n },\n },\n ],\n }),\n );\n}\n","/**\n * Build a fully-registered McpServer.\n *\n * Exported for embedding (and for the test suite to wire an in-memory transport).\n * The CLI module connects this to stdio; you can also connect it to any other\n * transport (HTTP/SSE, WebSocket) if the SDK provides one.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { registerResolveDay } from './tools/resolve-day.js';\nimport { registerResolvePeriod } from './tools/resolve-period.js';\nimport { registerEvaluateBreakCompliance } from './tools/evaluate-break-compliance.js';\nimport { registerApplyRounding } from './tools/apply-rounding.js';\nimport { registerGenerateRoster } from './tools/generate-roster.js';\nimport { registerListRulePacks } from './tools/list-rule-packs.js';\nimport { registerAuditPeriodCompliance } from './tools/audit-period-compliance.js';\nimport { registerDiagnosePunches } from './tools/diagnose-punches.js';\nimport { registerDocsResources } from './resources/docs.js';\nimport { registerAnalyseTimecardPrompt } from './prompts/analyse-timecard.js';\nimport { registerRosterPlannerPrompt } from './prompts/roster-planner.js';\n\nexport interface CreateServerOptions {\n /** Override the advertised server name (default: 'attendance-engine'). */\n name?: string;\n /** Override the advertised server version (default: matches package.json). */\n version?: string;\n}\n\nexport function createServer(options: CreateServerOptions = {}): McpServer {\n const server = new McpServer({\n name: options.name ?? 'attendance-engine',\n version: options.version ?? '0.1.0',\n });\n\n // Tools — the work surface.\n registerResolveDay(server);\n registerResolvePeriod(server);\n registerEvaluateBreakCompliance(server);\n registerApplyRounding(server);\n registerGenerateRoster(server);\n registerListRulePacks(server);\n registerAuditPeriodCompliance(server);\n registerDiagnosePunches(server);\n\n // Resources — docs + rule packs.\n registerDocsResources(server);\n\n // Prompts — guided workflows.\n registerAnalyseTimecardPrompt(server);\n registerRosterPlannerPrompt(server);\n\n return server;\n}\n","#!/usr/bin/env node\n/**\n * Stdio entrypoint. Designed to be launched by an MCP host\n * (Claude Desktop, Cursor, Windsurf, custom) and speak JSON-RPC over stdio.\n *\n * \"mcpServers\": {\n * \"attendance-engine\": {\n * \"command\": \"npx\",\n * \"args\": [\"-y\", \"@attendance-engine/mcp\"]\n * }\n * }\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createServer } from './server.js';\n\nasync function main(): Promise<void> {\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n // server now owns stdio; the process exits when the host disconnects.\n}\n\nmain().catch((err: unknown) => {\n // Errors during startup go to stderr — stdio MUST stay clean for the JSON-RPC stream.\n // eslint-disable-next-line no-console\n console.error('[attendance-engine-mcp] fatal:', err);\n process.exit(1);\n});\n"]}
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createServer } from './chunk-UACYGW4C.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
const server = createServer();
|
|
7
|
+
const transport = new StdioServerTransport();
|
|
8
|
+
await server.connect(transport);
|
|
9
|
+
}
|
|
10
|
+
main().catch((err) => {
|
|
11
|
+
console.error("[attendance-engine-mcp] fatal:", err);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=cli.js.map
|
|
15
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"names":[],"mappings":";;;;AAgBA,eAAe,IAAA,GAAsB;AACnC,EAAA,MAAM,SAAS,YAAA,EAAa;AAC5B,EAAA,MAAM,SAAA,GAAY,IAAI,oBAAA,EAAqB;AAC3C,EAAA,MAAM,MAAA,CAAO,QAAQ,SAAS,CAAA;AAEhC;AAEA,IAAA,EAAK,CAAE,KAAA,CAAM,CAAC,GAAA,KAAiB;AAG7B,EAAA,OAAA,CAAQ,KAAA,CAAM,kCAAkC,GAAG,CAAA;AACnD,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAChB,CAAC,CAAA","file":"cli.js","sourcesContent":["#!/usr/bin/env node\n/**\n * Stdio entrypoint. Designed to be launched by an MCP host\n * (Claude Desktop, Cursor, Windsurf, custom) and speak JSON-RPC over stdio.\n *\n * \"mcpServers\": {\n * \"attendance-engine\": {\n * \"command\": \"npx\",\n * \"args\": [\"-y\", \"@attendance-engine/mcp\"]\n * }\n * }\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createServer } from './server.js';\n\nasync function main(): Promise<void> {\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n // server now owns stdio; the process exits when the host disconnects.\n}\n\nmain().catch((err: unknown) => {\n // Errors during startup go to stderr — stdio MUST stay clean for the JSON-RPC stream.\n // eslint-disable-next-line no-console\n console.error('[attendance-engine-mcp] fatal:', err);\n process.exit(1);\n});\n"]}
|