Skip to main content
Glama
README.md
# ehour-mcp

An [MCP](https://modelcontextprotocol.io) server that fills and saves
[eHour](https://github.com/te-con/ehour) timesheets for you. Ask your agent "submit my June
hours": it drafts each day from your `git log`, shows you the month, and after your OK fills
the timesheet in a real Chrome window, saves it week by week and reads it back to check.

- **Drafts from commits.** `PROJ-123 Title  - detail` subjects become one entry per weekday;
  weekend commits move to the nearest free weekday.
- **Review before anything is saved.** The draft is a JSON file the agent shows and edits with you.
- **Verified saves.** Every week is read back after saving and compared with what you approved.
- **Small fixes.** `patch_day` corrects one day without touching the rest.
- **No stored credentials.** It attaches over the Chrome DevTools Protocol to a Chrome
  window where you logged in yourself.

See [docs/DESIGN.md](docs/DESIGN.md) for how it works and what it refuses to do.

## Requirements

- Node.js 20 or newer
- Google Chrome (any Chromium-based browser works via `cdpBrowser`)
- An eHour account with one project row on your timesheet
- An MCP client such as Claude Code

## Setup

1. Install dependencies:
   ```bash
   npm install
   ```
2. Create your config from the example and fill it in:
   ```bash
   cp ehour.config.example.json ehour.config.json
   ```

   | Key | Required | Meaning |
   |---|---|---|
   | `url` | yes | Your eHour timesheet page, e.g. `https://ehour.example.com/eh/consultant/overview?1&openPanel=TIMESHEET` |
   | `projectCode` | yes | The project code shown on your timesheet row, e.g. `PROJ` |
   | `ticketPrefixes` | no | Ticket keys to recognise in commit subjects, e.g. `["PROJ", "OPS"]`. Default: any `UPPERCASE-123` key |
   | `gitRepo` | no | Repository `draft_month` reads by default |
   | `cdpPort` | no | Chrome remote-debugging port, default `9237` |
   | `cdpProfile` | no | Chrome profile folder, default `~/.ehour-cdp-profile` |
   | `cdpBrowser` | no | Browser binary, default Google Chrome on macOS |
   | `host` | no | Host used to find the eHour tab, default taken from `url` |

   Every key can also be set as an environment variable (`EHOUR_URL`, `EHOUR_PROJECT_CODE`,
   `EHOUR_TICKET_PREFIXES` as a comma list, `EHOUR_GIT_REPO`, `EHOUR_CDP_PORT`, …), which wins
   over the file. `EHOUR_CONFIG` points to a different config file.
3. Start the dedicated Chrome and sign in to eHour once:
   ```bash
   ./scripts/start-ehour-chrome.sh
   ```
   It uses its own profile, so your normal browsing is untouched and the session survives restarts.
4. Register the server with your MCP client. For Claude Code:
   ```bash
   claude mcp add ehour -- node /absolute/path/to/ehour-mcp/src/server.mjs
   ```

## Use

- "Submit my June hours" → `draft_month` writes `drafts/<from>_<to>.json` from your commits and
  returns a summary. The agent shortens each day, fills the gaps, shows you the month, and on
  your OK calls `apply_month` with that file.
- "June 3 should say X" → `patch_day`.
- "What's in eHour for last week?" → `read_timesheet`.

| Tool | Purpose |
|---|---|
| `check_login` | Is the dedicated Chrome up and logged in? |
| `draft_month` | Draft entries from `git log` into a file; returns a summary only |
| `apply_month` | Fill and save entries, then read back and report differences |
| `verify_entries` | Compare an entries file with eHour; differences only |
| `patch_day` | Correct a single saved day |
| `read_timesheet` | Read saved hours and comments for a date range |

## Notes

- The server never closes Chrome. Quit it with `./scripts/stop-ehour-chrome.sh`.
- If the session expires, `check_login` says so: sign in again in that window.
- Every day gets 8 hours unless an entry says otherwise. Comments are converted to ASCII
  because eHour stores them in CP-1252.
- Tested against one eHour installation with the standard web UI. A customised skin may need
  the selectors in `src/ehour-dom.mjs` adjusted.

## Development

```bash
npm test        # unit tests: entries, week math, drafting, config
npm run smoke   # read-only checks against your live eHour: page | login | read FROM TO
```

## License

[MIT](LICENSE)

TDQS

A4.3/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a clearly distinct role in a single linear workflow: check_login (session state), draft_month (offline generation from git), apply_month (bulk write), verify_entries (file vs. server diff), patch_day (single-day correction), read_timesheet (live read). The only near-neighbor pair, verify_entries and read_timesheet, is separated by description (file comparison vs. raw live state).

Naming Consistency5/5

All six tools follow a strict verb_noun snake_case pattern (check_login, draft_month, apply_month, verify_entries, patch_day, read_timesheet) with no stylistic deviations. The verb choices (draft/apply/verify/patch/read/check) meaningfully encode the read-only vs. write vs. check semantics.

Tool Count5/5

Six tools is well-scoped for a focused timesheet-automation server; each tool maps to a necessary stage (preflight, draft, commit, verify, correct, inspect) with no filler. Nothing appears redundant or missing at the structural level.

Completeness4/5

The draft → apply → verify → patch → read lifecycle is covered end to end, but there is no explicit way to delete or clear an already-saved day (patch_day only updates hours/comment), nor any tool to discover projects or tickets the server is configured for. These are workaroundable gaps rather than dead ends, since hours could presumably be zeroed via patch_day.

Maintenance

ActivityMaintained
ResponsivenessNo issues