Skip to main content
Glama
README.md
<div align="center">

# ๐Ÿ•’ attendance-engine MCP

### Wage-and-hour answers your AI agent can actually trust.

**Ask Claude *"Did anyone miss a meal break last Tuesday?"* โ€” and have it actually be right.**

[![npm](https://img.shields.io/npm/v/@attendance-engine/mcp.svg?style=for-the-badge&color=4c8eda)](https://www.npmjs.com/package/@attendance-engine/mcp)
[![CI](https://img.shields.io/github/actions/workflow/status/arifur9993/attendance-engine-mcp/ci.yml?style=for-the-badge&label=tests)](https://github.com/arifur9993/attendance-engine-mcp/actions)
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](LICENSE)
[![MCP](https://img.shields.io/badge/MCP-1.x-purple.svg?style=for-the-badge)](https://modelcontextprotocol.io/)

</div>

---

## ๐Ÿค” The problem

Every HR / payroll / time-tracking team eventually asks Claude (or Cursor, or Windsurf) something like:

> *"Rahim's punches yesterday were 09:00, 13:00, 14:00, and 18:00. Did he get his meal break under California rules?"*

And the LLM does what LLMs do: it eyeballs the timestamps, mumbles something about "yes probably, his lunch looks fine," and moves on. Sometimes it's right. Sometimes it forgets that California requires the meal to *start before the end of the 5th hour*. Sometimes it counts a 25-minute break as compliant. Sometimes, for an overnight shift that crosses midnight, it just gives up.

**You can't put that in front of an auditor.** You can't ship it inside a payroll product. You can't trust it with overtime calculations that turn into back-pay liability if they're wrong.

## ๐Ÿ’ก What this is

A small **Model Context Protocol** server that gives your AI agent **deterministic, tested, fixture-backed tools** for:

- Resolving a duty day from raw clock punches (overnight, breaks, OT, all of it).
- Auditing meal/rest compliance under the **California** rule pack (Labor Code ยงยง 226.7, 512; IWC wage orders), including *Donohue v. AMN* rebuttable-presumption signals.
- Rounding worked time without losing the exact-minute baseline (so you can prove your rounding is neutral).
- Building rotating rosters: 2-2-3, 4-on-4-off, DuPont, Pitman.
- Triaging suspicious punch streams *before* you trust them.
- Running a multi-day **wage-and-hour audit** across a whole pay period and rolling up premium hours owed, days at risk, and the flag heatmap.

The math lives in [`@attendance-engine/core`](https://www.npmjs.com/package/@attendance-engine/core) โ€” a pure-function, zero-deps TypeScript library with 100% test coverage. This MCP server is the thin agent surface on top.

## ๐Ÿง  How it actually works

```mermaid
flowchart LR
    A[You: "Did Rahim miss his meal break last Tuesday?"]
    B[Claude / Cursor / Windsurf]
    C[attendance-engine MCP]
    D[("@attendance-engine/core
    pure-function engine
    100% coverage")]

    A -->|prompt| B
    B -->|tool call| C
    C -->|function call| D
    D -->|"DayResult + ComplianceResult"| C
    C -->|"JSON content block"| B
    B -->|"plain-English answer with citations"| A

    classDef user fill:#0b3d91,stroke:#fff,color:#fff
    classDef host fill:#5b1ea3,stroke:#fff,color:#fff
    classDef mcp fill:#1f6f43,stroke:#fff,color:#fff
    classDef core fill:#7c4a03,stroke:#fff,color:#fff
    class A user
    class B host
    class C mcp
    class D core
```

Two important properties:

1. **Claude doesn't do the math.** It picks a tool, fills the arguments, and forwards the answer. If the engine says "this was a late meal," the agent says "this was a late meal." If you re-ask the same question, you get the same answer โ€” every time.
2. **Time zones are explicit, not guessed.** Every timestamp carries its own offset. The engine never reads the host clock, never assumes UTC, never silently converts. DST days work because *you* told it the offset, not because it inferred it.

## ๐Ÿš€ Install โ€” pick your host

Pick the MCP host you're already using. Same one-liner everywhere:

<details>
<summary><b>Claude Desktop (macOS / Windows)</b></summary>

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS or `%APPDATA%\Claude\claude_desktop_config.json` on Windows:

```json
{
  "mcpServers": {
    "attendance-engine": {
      "command": "npx",
      "args": ["-y", "@attendance-engine/mcp"]
    }
  }
}
```

Fully quit and relaunch Claude Desktop (Cmd-Q on macOS โ€” closing the window isn't enough).
</details>

<details>
<summary><b>Cursor</b></summary>

`~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "attendance-engine": {
      "command": "npx",
      "args": ["-y", "@attendance-engine/mcp"]
    }
  }
}
```
</details>

<details>
<summary><b>Windsurf</b></summary>

`~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "attendance-engine": {
      "command": "npx",
      "args": ["-y", "@attendance-engine/mcp"]
    }
  }
}
```
</details>

<details>
<summary><b>Any other MCP host</b></summary>

Launch the stdio binary directly:

```bash
npx -y @attendance-engine/mcp
```

The server speaks JSON-RPC over stdin/stdout โ€” that's the whole contract.
</details>

## ๐ŸŽฌ See it in action

> **๐Ÿ–ผ๏ธ Scenario cards below are publish-ready SVG mockups** (render inline on GitHub, npm, LinkedIn). Real-product screenshots can replace them when you capture them โ€” see [`docs/screenshots/README.md`](docs/screenshots/README.md).

### Scenario 1 โ€” Did the night-shift nurse get her meal break?

![night-shift compliance demo](docs/screenshots/01-night-shift-compliance.svg)

> **You:** *"Sara was on the 22:00โ†’06:00 shift on June 1st. Punches: 21:54, 02:00, 02:30, 06:18. Apply California rules."*
>
> **Claude (calls `evaluate_break_compliance`):** *"Sara worked 7h54m on her duty date of June 1st (her clock-out fell on June 2nd but belongs to this shift). She took a 30-minute meal beginning at her 4-hour mark, which is compliant under California's mandate to start the first meal before the end of the 5th hour. Worked time spans midnight; flagged accordingly. She left 18 minutes past schedule โ€” that 18m counts as overtime."*

### Scenario 2 โ€” Find every late-meal day this month

![multi-day audit demo](docs/screenshots/02-period-audit.svg)

> **You:** *"Run an `audit_period_compliance` for employee EMP-007 across June 2026. Here are 30 days of punches and shifts. Use California."*
>
> **Claude:** *"3 days at high presumption risk โ€” June 4, June 12, June 22. All three are late meals (started past the 5th hour). Total meal-period premium hours owed: 3. June 22 also drops below the 12-hour second-meal threshold. No rest-period violations detected."*

### Scenario 3 โ€” Are these punches even usable?

![diagnose punches demo](docs/screenshots/03-diagnose-punches.svg)

> **You:** *"I have punches 09:00, 13:00, 14:00 for June 1st. Can I trust these?"*
>
> **Claude (calls `diagnose_punches`):** *"Recommendation: **reject**. The punch count is odd โ€” one in or out is missing. Without that, worked-time and overtime would be guesses. Also: every punch falls on a 5-minute boundary, which often indicates manual entry rather than a device read. Worth verifying with the source system before resolving."*

### Scenario 4 โ€” Plan a 4-on-4-off roster for next month

![roster demo](docs/screenshots/04-roster.svg)

> **You:** *"Build me a 4-on-4-off roster for July 2026."*
>
> **Claude (calls `generate_roster`):** *"31 days โ€” work days on July 1โ€“4, 9โ€“12, 17โ€“20, 25โ€“28 (12h day shifts, 07:00โ€“19:00). Rest days fill the gaps. Want me to assign team rotations across this pattern?"*

## ๐Ÿ› ๏ธ Tools at a glance

| Tool | When you'd use it |
|---|---|
| **`resolve_day`** | "What happened on this single day? Lateness, OT, segments, flags." |
| **`resolve_period`** | "Roll up a week or a month: per-day results plus an aggregated summary." |
| **`evaluate_break_compliance`** | "Did this person get their meal/rest breaks under California law? Is any premium owed?" |
| **`audit_period_compliance`** | "Audit a whole pay period. Show me total premium hours, high-risk days, and the flag heatmap." |
| **`apply_rounding`** | "Round worked/OT minutes to a unit โ€” and keep the exact view alongside it so I can prove neutrality." |
| **`diagnose_punches`** | "Triage this raw punch stream. Should I trust it?" |
| **`generate_roster`** | "Build a 2-2-3 / 4-on-4-off / DuPont / Pitman / custom rotation." |
| **`list_rule_packs`** | "What jurisdictions are supported?" *(currently CA; more arrive in minor releases)* |

## ๐Ÿ“š Resources & prompts

Resources you can paste into a chat:

| URI | What it is |
|---|---|
| `attendance://docs/overview` | One-pager about the engine, time-zone rules, and how the tools compose. |
| `attendance://docs/api` | Compact field-by-field API reference. |
| `attendance://rules/CA` | The California rule pack as JSON โ€” meal/rest thresholds, waiver limits, premium caps, the citation source. |

Guided prompts (the host's `/` menu, or `prompts/get`):

- **`analyse_timecard`** โ€” walks the model through the right tool calls to analyse a single duty day.
- **`roster_planner`** โ€” generates a roster and renders it as a Markdown table.

## ๐Ÿ•ฐ๏ธ The time-zone rule (read this once and you're fine)

Every ISO timestamp must carry its own offset:

- โœ… `2026-06-01T08:57:00+06:00`
- โœ… `2026-06-01T08:57:00Z`
- โŒ `2026-06-01T08:57:00` *(rejected โ€” the engine won't guess)*

The engine reduces everything to absolute instants on a single timeline. DST works because the offsets are explicit. The duty date and shift `HH:MM` are worksite local wall-clock โ€” match them to your business calendar, not to UTC.

For days with no punches (an absence, a holiday), pass `policy.tzOffsetMinutes` explicitly so the engine has something to anchor the shift window to.

## ๐Ÿค Embedding (advanced)

Building your own host? Skip the CLI:

```ts
import { createServer } from '@attendance-engine/mcp';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

const server = createServer({ name: 'my-hr-server', version: '1.0.0' });
await server.connect(new StdioServerTransport());
```

Use it for: custom HTTP/SSE adapters, Claude Agent SDK setups, test harnesses, in-house deployments where the binary needs to live inside a bigger Node process.

## ๐Ÿ’ช What it's good for

- Internal HR / payroll / workforce-analytics chat assistants
- Audit-prep workflows for California employers
- Customer-support tools at HR-tech vendors who need their AI to actually be right
- Pre-payroll compliance triage ("which days need a human to review?")
- Schedule planners that need a real roster engine, not vibes

## ๐Ÿงฑ What it's not

- A leave-balance / accrual system (the engine deals in minutes, not entitlements).
- A payroll-money calculator (it gives you the hour buckets โ€” *you* multiply by the rate).
- A biometric device protocol (pair it with whatever ingest layer you've got).
- A UI. There's no dashboard in here; that's a separate concern.

## ๐ŸŒ Compatibility

| | |
|---|---|
| **Node** | 18+ (CI runs 20 LTS) |
| **MCP SDK** | 1.x |
| **Engine** | `@attendance-engine/core` โ‰ฅ 0.4 (peer dep) |
| **Hosts tested** | Claude Desktop 1.x ยท Cursor ยท Windsurf ยท any stdio MCP client |

## ๐Ÿ“– More reading

- ๐Ÿ“‘ [Detailed scenarios with full tool transcripts](docs/scenarios.md)
- ๐Ÿ–ผ๏ธ [How to capture your own demo screenshots](docs/screenshots/README.md)
- ๐Ÿ“š [Engine API reference](https://github.com/arifur9993/attendance-engine/blob/main/packages/core/docs/api.md)
- ๐ŸŒ [Time-zone semantics](https://github.com/arifur9993/attendance-engine/blob/main/packages/core/docs/timezones.md)
- ๐Ÿ›๏ธ [California Labor Code ยงยง 226.7, 512](https://www.dir.ca.gov/dlse/faq_mealperiods.htm)
- ๐Ÿ›๏ธ [Donohue v. AMN Services (Cal. 2021)](https://law.justia.com/cases/california/supreme-court/2021/s253677.html)

## โค๏ธ Credits

Built by [Md. Arifur Rahman](https://www.linkedin.com/in/md-arifur-rahman-mar/). Companion to [`@attendance-engine/core`](https://github.com/arifur9993/attendance-engine) (TypeScript) and [`arifur9993/attendance-engine`](https://github.com/arifur9993/attendance-engine-php) (PHP). Same author, same fixtures, same answers โ€” in three places your stack can reach for.

## License

MIT โ€” see [LICENSE](LICENSE).

TDQS

A4/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a distinct operation (rounding, auditing, diagnosing, evaluating, generating, listing, resolving single day, resolving period) with clear and non-overlapping purposes. Even similar tools like audit_period_compliance and evaluate_break_compliance are differentiated by scope (period vs single day, comprehensive audit vs specific break analysis).

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (e.g., apply_rounding, diagnose_punches, list_rule_packs). There is no mixing of conventions or vague verbs.

Tool Count5/5

The 8 tools cover the core workflows of an attendance engine (punch triage, day/period resolution, compliance, rounding, roster generation, rule pack listing) without being excessive. The count is well-scoped for the domain.

Completeness4/5

The tool surface covers most essential operations: raw punch diagnosis, day/period resolution, compliance audits, rounding, roster generation, and rule pack listing. Minor gaps exist, such as the lack of a tool to apply rounding to a period or to modify rule packs, but the set is functional for typical use cases.

Maintenance

ActivityInactive
ResponsivenessNo issues