timermcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@timermcpstart a 25-minute pomodoro"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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), 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.
Related MCP server: productive-mcp
Setup
Prerequisite: Node.js โฅ 18 (node --version).
TimerMCP is on npm โ no clone or build needed.
Claude Code (one command):
claude mcp add timermcp -- npx -y timermcp
claude mcp list # should show timermcp โ connectedClaude Desktop โ edit claude_desktop_config.json:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"timermcp": {
"command": "npx",
"args": ["-y", "timermcp"]
}
}
}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.jsOn Windows use forward slashes or escaped backslashes in config paths: "C:/tools/timermcp/dist/index.js".
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" |
| Monotonic start stamp + announcer call |
"start a 25-minute pomodoro" |
| Countdown mode; stop reports over/under |
"race me vs the build: start alice and bob" |
| Both start on the same nanosecond |
"split โ hero section done" |
| LiveSplit-style lap, delta vs PB pace ๐ข๐ด |
"how long so far?" |
| Live server-side reading |
"stop the clock" |
| Official ms, PB check, SVG result card |
"false start, cancel it" |
| Discards without touching history |
"what's my record?" |
| PBs, recent runs, lifetime totals |
"switch announcer to drill sergeant" |
| 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 asrecovered).
Environment
Var | Default | |
|
| Dashboard port |
|
| Set |
|
| State + cards directory |
| unset |
|
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 ownTIMERMCP_HOMEif you want separate PBs โ otherwise state.json is last-write-wins between them).node dist/index.jsseems to hang โ normal; stdio servers wait for a client. Use--versionto sanity-check.Times look ~equal but off by a constant โ that's differential latency; set
start_offset_ms/stop_offset_msviatimer_config(see latency math above).
Test
npm run smoke # 19 assertions: protocol, timing accuracy, offsets, race ฮ, dashboard, cardsMIT.
Available Tools
7 toolstimer_cancelCancel TimerADestructive
Discard a running timer WITHOUT recording a session, PB, or history entry. Use for false starts ("wait, I wasn't ready"). The would-be elapsed is returned for reference only.
Args:
name (string, default "main"): Timer to discard.
Returns: { name, discarded_elapsed_ms }
Inputs: name. Returns: name, discarded_elapsed_ms
timer_configConfigure TimerMCPAIdempotent
Read or update TimerMCP settings. Call with no args to read.
Settings:
announcer ('hype'|'zen'|'sergeant'|'sports'|'minimal'): Default announcement personality. Styles: hype, zen, sergeant, sports, minimal.
start_offset_ms / stop_offset_ms (number โฅ 0): Latency handicaps subtracted from every elapsed. Leave at 0 for most uses โ symmetric pipeline latency already cancels because both timestamps are taken server-side on a monotonic clock. Set these only to bias out a known asymmetry (e.g. user reckons their "stop" reaches the server ~300ms slower than their "start").
max_hours (number): A running timer older than this is flagged runaway (default 4).
sync_token (string): Enables cloud auto-sync. The user gets this token from their dashboard at https://timermcp.vercel.app/app ("set my timer sync token to โฆ"). Every finished run then syncs automatically. Pass an empty string to disable sync.
sync_url (string): Override the cloud endpoint (default https://timermcp.vercel.app).
Returns: { config: {announcer, start_offset_ms, stop_offset_ms, max_hours, sync_enabled, sync_token_tail?, sync_url?}, dashboard_url, data_dir }
Inputs: sync_url, announcer, max_hours, sync_token, +2 more. Returns: config, data_dir, dashboard_url
timer_historyHistory & Personal BestsARead-onlyIdempotent
Personal bests, recent finished runs, and lifetime totals. Read-only.
Args:
name (string, optional): Filter to one timer name; omit for everything.
limit (number, default 10): Max recent sessions returned (1โ50).
Returns: { bests: [{name, best_ms?, count, total_ms}], recent: [{id, name, elapsed_ms, formatted, pb, splits, stopped_wall, target_ms?, over_under_ms?}], lifetime_ms, lifetime_sessions }
Great for "what's my record?", "how much have I tracked today?", or building a leaderboard recap for the user.
Inputs: name, limit. Returns: bests, recent, lifetime_ms, lifetime_sessions
timer_splitRecord Split / LapA
Record a split (lap) on a running timer without stopping it โ speedrun style.
If a personal-best run exists for this timer name, the split is compared against the SAME split index on the PB run and delta_vs_best_ms is returned (negative = ahead of PB pace, like LiveSplit green).
Args:
name (string, default "main"): Running timer to split.
label (string, optional): Name the checkpoint, e.g. "deploy done", "lap 2". Defaults to "Split N".
Returns: { name, index, label, at_ms, formatted, delta_vs_best_ms?, announcement }
Relay the announcement; if delta_vs_best_ms is present, call out ahead/behind PB pace.
Inputs: name, label. Returns: name, at_ms, index, label, +3 more
timer_startStart TimerA
Start a millisecond-precision stopwatch (or countdown) on the TimerMCP server.
The server stamps the start on a MONOTONIC nanosecond clock the instant this call arrives. Elapsed time is computed server-side as stop-stamp minus start-stamp, so the constant part of the pipeline delay (user โ Claude โ server) cancels out between start and stop โ only the difference in latency between the two calls can leak in.
Args:
name (string, default "main"): Timer name. NAME IT AFTER THE TASK โ when the user says what they are working on ("start a timer for the auth refactor"), use that as the name ("auth refactor") so their session log reads like a work journal. Pass a comma-separated list (e.g. "alice,bob") to start several timers on the SAME nanosecond โ race mode.
target_ms (number, optional): Turns the run into a countdown (Pomodoro, rest interval, speedrun target). Stop reports over/under.
announcer ('hype'|'zen'|'sergeant'|'sports'|'minimal', optional): Voice for this run's announcements. Defaults to configured style.
start_offset_ms (number, optional): Milliseconds subtracted from the final elapsed to bias out start-side latency. Defaults to configured value (0).
Returns: { started: [{name, elapsed_ms, started_wall, target_ms?}], announcement, dashboard_url }
IMPORTANT: Relay the 'announcement' line to the user with matching energy โ you are the track announcer. Mention the dashboard_url the first time so they can put the live wall display on a second screen.
Errors: fails if a timer with the same name is already running (stop or cancel it first, or pick another name).
Inputs: name, announcer, target_ms, start_offset_ms. Returns: started, announcement, dashboard_url
timer_statusTimer StatusARead-onlyIdempotent
Check what's on the clock right now. Read-only.
Args:
name (string, optional): One timer, or omit for all running timers.
Returns: { running: [{name, elapsed_ms, formatted, target_ms?, remaining_ms?, splits, best_ms?, recovered, runaway}], count, dashboard_url } 'recovered' = true means the server restarted mid-run and that timer fell back to wall-clock precision. 'runaway' = true means the timer has been running longer than the configured max_hours (default 4) โ the user probably forgot it. Ask if they want to stop it, offering trim_to_ms to cap the recorded time at what they actually worked. elapsed_ms is a live server-side reading at the moment this call was processed.
Inputs: name. Returns: count, running, dashboard_url
timer_stopStop TimerA
Stop a running timer and get the official elapsed time in milliseconds.
elapsed_ms = (monotonic stop-stamp โ monotonic start-stamp) โ start_offset_ms โ stop_offset_ms. raw_elapsed_ms is the pure unadjusted monotonic difference. Because both stamps are taken server-side, symmetric pipeline latency cancels; offsets exist only to bias out asymmetry if the user wants.
Args:
name (string, default "main"): Which timer to stop.
stop_offset_ms (number, optional): Override the configured stop-side latency handicap for this stop only.
trim_to_ms (number, optional): Cap the recorded time. Use when the user forgot to stop a runaway timer and tells you how long they actually worked (e.g. "I only worked 25 minutes" โ trim_to_ms 1500000). The session records the capped time and is flagged trimmed.
Returns: { session: {name, elapsed_ms, raw_elapsed_ms, formatted, splits, pb, pb_margin_ms?, target_ms?, over_under_ms?, id}, announcement, card_path, card_url }
A shareable SVG result card is written on every stop (card_path on disk, card_url served by the dashboard).
IMPORTANT: Relay the 'announcement' to the user โ big finish energy if it's a PB. Quote the exact millisecond figure.
Inputs: name, trim_to_ms, stop_offset_ms. Returns: session, card_url, card_path, announcement
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
TDQS
Scored across 7 tools
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.
All tools follow a consistent 'timer_verb' pattern in lowercase snake_case, making the naming predictable and easy to understand.
Seven tools cover the essential timer operations without being excessive. Each tool adds unique value, and the count feels well-scoped for the domain.
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
Related MCP Connectors
timesheet.io MCP server - manage timers, projects, tasks and reports
Privacy-first time tracking for freelancers. EU-hosted, with an MCP server for AI assistants.
A time server that keeps your AI honest about time. Real clock + drift guard, zero dependencies.
Hosted text-to-speech MCP server: 66 voices in 9 languages, long-form narration, podcasts.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server that provides interval timing functionality using token-based time tracking, allowing users to start timers with unique identifiers and check elapsed time in milliseconds or human-readable format.Apache 2.0
- AlicenseAqualityDmaintenanceAn MCP server for Productive.io that enables users to log time, inspect projects, and manage time entries using natural language commands. It features fuzzy project matching, local caching, and remembers default services per project for streamlined time tracking.14MIT
- FlicenseNot gradedqualityDmaintenanceMCP server that provides tools to control a server-owned Pomodoro timer, allowing focus, pause, resume, skip, reset, and stats operations.1-
- AlicenseNot gradedqualityDmaintenanceAn MCP server for time tracking and billing that allows freelancers to control timers, manage projects, and export invoices via natural language in any MCP-compatible AI assistant.6 npmMIT