timermcp
by julanbasnet
README.md
# TimerMCP โฑ โ the talking stopwatch for Claude
A millisecond-precision MCP timer server. Say "start the timer" to Claude โ the clock starts on the server's monotonic nanosecond clock, a live wall display lights up in your browser, and when you say "stop," Claude announces the official time like a track announcer.
```
๐ STOP THE CLOCK! 'ship the landing page' finishes in 4:07.312!
๐ NEW PERSONAL BEST โ 18.455 faster than the old record!
```
With the free cloud connector ([timermcp.com](https://timermcp.com)), countdowns render as a live ticking card right in the chat โ desktop and mobile:

## Why the milliseconds are honest (the latency math)
You say "start" at **tโ**; the command reaches the server after delay **dโ** (typing + model + transport). Server stamps **Sโ = tโ + dโ**. You say "stop" at **tโ**, stamped **Sโ = tโ + dโ**.
```
reported = Sโ โ Sโ = (tโ โ tโ) + (dโ โ dโ)
```
Both timestamps are taken **server-side**, so the constant part of the pipeline delay **cancels**. Only the *difference* between start-side and stop-side latency can leak in โ and for symmetric calls that's small. All math runs on `process.hrtime.bigint()` (monotonic nanoseconds), immune to NTP jumps and DST.
If you believe your pipeline is asymmetric, bias it out: `timer_config` accepts `start_offset_ms` / `stop_offset_ms`, subtracted from every result. `raw_elapsed_ms` (pure monotonic) is always returned alongside the adjusted `elapsed_ms`.
## Setup
**Prerequisite:** Node.js โฅ 18 (`node --version`).
TimerMCP is on npm โ no clone or build needed.
**Claude Code** (one command):
```bash
claude mcp add timermcp -- npx -y timermcp
claude mcp list # should show timermcp โ connected
```
**Claude Desktop** โ edit `claude_desktop_config.json`:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"timermcp": {
"command": "npx",
"args": ["-y", "timermcp"]
}
}
}
```
<details>
<summary>Running from a clone instead (for development)</summary>
```bash
git clone https://github.com/julanbasnet/timermcp.git
cd timermcp
npm install && npm run build
node dist/index.js --version # prints banner and exits
npm run smoke # full 18-assertion suite
claude mcp add timermcp -- node /absolute/path/to/timermcp/dist/index.js
```
On Windows use forward slashes or escaped backslashes in config paths: `"C:/tools/timermcp/dist/index.js"`.
</details>
After adding, fully quit and reopen the client (Desktop: quit from the menu/tray, not just close the window). Say *"start the timer"* and open **http://localhost:5717** โ the wall display.
**Inspector (optional):** `npx @modelcontextprotocol/inspector npx -y timermcp` for an interactive tool console.
## Talk to it
| You say | Tool | What happens |
|---|---|---|
| "start the timer" | `timer_start` | Monotonic start stamp + announcer call |
| "start a 25-minute pomodoro" | `timer_start` (`target_ms`) | Countdown mode; stop reports over/under |
| "race me vs the build: start alice and bob" | `timer_start` (`"alice,bob"`) | Both start on the **same nanosecond** |
| "split โ hero section done" | `timer_split` | LiveSplit-style lap, delta vs PB pace ๐ข๐ด |
| "how long so far?" | `timer_status` | Live server-side reading |
| "stop the clock" | `timer_stop` | Official ms, PB check, SVG result card |
| "false start, cancel it" | `timer_cancel` | Discards without touching history |
| "what's my record?" | `timer_history` | PBs, recent runs, lifetime totals |
| "switch announcer to drill sergeant" | `timer_config` | hype ยท zen ยท sergeant ยท sports ยท minimal |
## The viral bits
- **Live wall display** โ electric-cyan timing console at `localhost:5717`, streams every millisecond over SSE. Put it on a second monitor or your phone; it reacts the instant Claude acts. Buttons + keyboard (space = split, enter = start/stop) work from the browser too, including a countdown-target field for browser-started pomodoros.
- **Countdown chime** โ flip the ๐ Sound toggle on the wall and every countdown rings out loud the moment it hits zero. (Off by default; browsers require one click before a page may play audio.)
- **XP, levels & achievements** โ every finished run and tracked minute earns XP. Ten ranks from ROOKIE to TIME LORD, a day-streak counter, and a 12-medal achievement shelf (PHOTO FINISH, RECORD BREAKER, BULLSEYE, MIDNIGHT OIL, RACE DAYโฆ), all derived from your local history. Level-ups and unlocks fire toasts + confetti on the wall.
- **Personal bests & speedrun splits** โ every named timer keeps a PB. Splits compare against the same checkpoint on your PB run, LiveSplit-style: *"1.204 AHEAD of PB pace ๐ข"*. Confetti on new records.
- **Race mode** โ comma-separated names start on one shared nanosecond stamp (measured ฮ in tests: 0.003ms).
- **Result cards** โ every stop writes a 1200ร630 SVG finish card (`~/.timermcp/cards/`, also served at `/card/{id}`). Screenshot-ready for X/LinkedIn.
- **Announcer personalities** โ hype race-caller by default; zen, drill sergeant, broadcast, or minimal.
- **Crash-safe** โ state persists to `~/.timermcp/state.json`; timers running across a restart recover on wall-clock precision (flagged as `recovered`).
## Environment
| Var | Default | |
|---|---|---|
| `TIMERMCP_PORT` | `5717` | Dashboard port |
| `TIMERMCP_HOST` | `127.0.0.1` | Set `0.0.0.0` to view from other devices on your LAN (no auth โ trusted networks only) |
| `TIMERMCP_HOME` | `~/.timermcp` | State + cards directory |
| `TIMERMCP_NO_DASHBOARD` | unset | `1` disables the HTTP server |
## Troubleshooting
- **Tools don't appear in Claude** โ the config path must be absolute; fully quit and relaunch the client. Server logs (stderr) land in the client's MCP logs โ macOS: `~/Library/Logs/Claude/mcp-server-timermcp.log`, Windows: `%APPDATA%\Claude\logs\`.
- **Dashboard won't load / "port taken" in logs** โ another TimerMCP instance owns 5717. Claude Desktop and Claude Code each spawn their *own* server process; timers still work in both, but give the second one `TIMERMCP_PORT=5718` (and its own `TIMERMCP_HOME` if you want separate PBs โ otherwise state.json is last-write-wins between them).
- **`node dist/index.js` seems to hang** โ normal; stdio servers wait for a client. Use `--version` to sanity-check.
- **Times look ~equal but off by a constant** โ that's differential latency; set `start_offset_ms` / `stop_offset_ms` via `timer_config` (see latency math above).
## Test
```bash
npm run smoke # 19 assertions: protocol, timing accuracy, offsets, race ฮ, dashboard, cards
```
MIT.
TDQS
A4.5/5.0
Scored across 7 tools
Disambiguation5/5
Each tool has a clear, distinct purpose: start, stop, cancel, split, status, history, and config. There is no functional overlap, and the descriptions explicitly differentiate them.
Naming Consistency5/5
All tools follow a consistent 'timer_verb' pattern in lowercase snake_case, making the naming predictable and easy to understand.
Tool Count5/5
Seven tools cover the essential timer operations without being excessive. Each tool adds unique value, and the count feels well-scoped for the domain.
Completeness4/5
The tool set covers the full lifecycle of timer operations: start, stop, cancel, split, live status, history, and configuration. Minor gaps like deleting history or editing sessions are absent but not critical for the core use case.
Maintenance
ActivityStale
ResponsivenessNo issues