Skip to main content
Glama

ehour-mcp

An MCP server that fills and saves 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 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

Related MCP server: Timesheet Assistant MCP

Setup

  1. Install dependencies:

    npm install
  2. Create your config from the example and fill it in:

    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:

    ./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:

    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

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

Available Tools

6 tools
apply_monthA

Fill and SAVE eHour timesheet entries for the configured project, one working day per entry. Groups entries by week, navigates each week, fills hours + the day comment, and saves per week (persisted as it goes), then reads the range back and compares it. Pass entriesFile (the draft_month file) instead of inline entries. Returns { weeks: [{ weekOf, savedDates, warnings, error?, filledButUnsaved? }], stoppedAt?, verification: { ok, checked, mismatches, unexpected } }. If a week fails, earlier weeks stay saved and stoppedAt names the failed week. Review the entries with the user BEFORE calling this — it commits to the timesheet.

Inputs: verify, entries, entriesFile.

check_loginA

Check whether the dedicated eHour Chrome is running and logged in. Returns { loggedIn, currentUrl, message }. Call this before apply_month/patch_day; if loggedIn is false, tell the user to sign in to the dedicated Chrome window.

Inputs: no named parameters.

draft_monthA

Draft a timesheet from git commit messages (read-only, touches no eHour page). One entry per weekday in the range, 8h each: "[8] TICKET: Title" then " - bullet" lines from the commit subjects, ASCII-only. Weekend commits move to the nearest empty weekday. Writes the entries JSON to file and returns only a summary { file, days: [{ date, tickets, chars }], moved, gaps }. Read the file, shorten each comment to 1-3 outcome bullets per ticket, fill every gap (a gap has an empty comment and is rejected by apply_month), then show the month to the user.

Inputs: to (required), from (required), repo, author, +1 more.

patch_dayA

Correct a single already-saved day for the configured project: navigate to its week, update hours and/or comment, save. Does NOT touch any other day. Returns { date, saved, error?, before, after }. Provide at least one of hours/comment.

Inputs: date (required), hours, comment.

read_timesheetA

Read back the currently-saved project hours + comments for a date range (inclusive). Read-only. Returns { days: [{ date, hours, comment }] } for days that have any hours or comment. Use to show the live state before/after a patch.

Inputs: to (required), from (required).

verify_entriesA

Compare an entries file with what eHour holds for its date range (read-only). Returns { ok, checked, mismatches: [{ date, field, expected?, actual? }], unexpected: [dates saved but not in the file] }.

Inputs: entriesFile (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    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
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers