estimate-maestro
An MCP server for recording effort estimates, plans, time intervals, absences, and commits, then reading back calibration data without estimating for you.
Start efforts with a plan and initial PERT estimate; add, close, drop, or rename slices.
Clock in/out, annotate or repair intervals, and record absences retroactively.
Record revised estimates, final plans with counterfactual final estimates, harvested/landed/remapped commits, and finish efforts.
Check what is currently clocked in.
View prior efforts' summaries and estimates for comparison, with no actuals.
Read calibration outputs: an effort table with wall hours/calendar days/slice counts, per-effort detail history, and aggregate summary or honest refusal when data is insufficient.
Harvests commit metadata from a local Git working tree into the estimate store, records durable commits that landed on the default branch, and remaps rewritten commit ids onto their replacements, letting clocked work be tied back to actual commits.
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., "@estimate-maestro Log an initial PERT estimate for the current effort."
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.
estimate-maestro
A personal MCP server and command-line tool for recording estimates and wall time, so that estimates can eventually be checked against outcomes instead of against feeling.
Today it is a data collection tool for estimation calibration, not an estimator: it does not estimate anything for you. It records what you estimated a piece of work at, what the plan turned out to be, and how much wall time the work actually took. What you do with that record — comparing, noticing drift, deciding you were consistently optimistic about a certain kind of work — is up to you and to whatever reads the store later. Through version 1.0.0 the tool's job stops at recording honestly.
That is the scope of the first version, not the scope of the project. Helping you make your own estimates, and eventually handing the estimating itself to an LLM or something like it, are both in scope later — and both need this record to exist first.
The model
Four kinds of thing get recorded:
Effort — a piece of work that is supposed to be estimated: a pull request, a mega-PR, anything with a plan and an estimate. An effort exists from the moment it has a plan artefact (a spec, a
.PR.md, whatever the plan lives in) and an initial estimate. Before that point there is nothing to record yet. The estimate excludes the time spent making the plan: recording starts once the plan exists, so counting the planning time in the estimate would skew the feedback.Slice — a piece of an effort, one step of the plan. Slices are added, closed against a pull request, dropped, or renamed. A slice added before the effort starts is part of the original plan; one added afterwards is scope growth, and the two are kept distinguishable in the record.
Interval — a clocked-in span of time, ordinarily against a slice, or against the effort itself when there is nothing finer to clock against. Intervals are closed by clocking out.
Absence — time that was not worked at all: a holiday, an appointment, an unrelated meeting. Absences can be recorded retroactively and are expected to be.
Related MCP server: time-tracker-ai-mcp
The five things
Five pieces of data are what the tool exists to produce:
the initial estimate;
the initial plan;
the final estimate;
the final plan — the plan you actually executed;
the total wall time taken, by the one person working on the effort.
Everything else that gets recorded is in support of those five. The nature of the recording means other statistics could be collected too, but they are by-products rather than the artefacts the tool is built to deliver.
This matters because the later work on helping with estimates will be built on those five things, and any system that can provide them will serve equally well.
Estimates: three-point triples in points
Every estimate is a PERT triple — optimistic, likely, pessimistic — given on an abstract points scale, never in hours. The expected value and the standard deviation are derived from the triple on read; nothing stores a single "the estimate is N" number, because a bare number throws away the uncertainty that made it an estimate rather than a fact.
Points are anchored by comparing the work at hand to prior efforts of similar shape, not by converting a felt number of hours into points in your head. The whole point of an abstract scale is to compare like with like across efforts whose actual duration you don't yet know.
Which scale is left entirely to you. Any numeric, roughly linear scale will do — this is a single-person tool, so the points only ever have to mean something next to your own earlier estimates. Even consistency over time is a convenience rather than a requirement: if you would size the same work at 50 points one year and 70 the next, the record still tells you what you need to know.
Two estimates are what the tool actually uses:
The initial estimate, made against the plan as first written, covering the slices the plan started with rather than the ones it grew.
The final estimate, made at the retrospective against the plan as it actually turned out — including every slice inserted along the way. This is a counterfactual: "given everything you now know about how this actually shook out, what would you have estimated?" — never "how long did it take?", and never a measurement of anything.
Splitting the estimate into two passes is what makes the two ways an estimate can go wrong separable after the fact. The initial pass can be wrong because of inventory error — work existed that nobody knew about yet, or work that was assumed to exist turned out not to. The final pass, made against the plan as it actually turned out, isolates sizing error — the items that were known about all along and simply judged wrongly. A single number per effort conflates the two; two numbers, one per pass, keep them apart.
Revised estimates can also be recorded mid-effort — typically when a slice is inserted that was not in the original plan — without waiting for the retrospective. Re-estimating as the plan grows is a healthy part of the process, so the revisions are kept; but nothing reads them back yet, and only the initial and the final estimate feed the record. The data is there in case a use for the middle turns up.
The final re-estimate has a second purpose: it trains the skill. Given what you now know, how should you estimate work of this shape next time?
The time model: a normal day is assumed
A normal working day is assumed by default. Clocking in and out is the ordinary workflow. A missed clock-out leaves the timer running until something closes it, and the working-hours model decides where it is closed.
The assumed day is 09:00–17:00, with one hour of lunch, Monday to Friday. That is under eight hours on purpose: the shortfall accounts for meetings and other time not spent on the project.
Manually clocked time counts in full, whatever hours it falls in. Evenings and weekends included: if you clocked in at 22:00 on a Saturday, that is what gets recorded, with no working-hours adjustment applied to it.
The working-hours model only ever applies where a timestamp is missing: ending a forgotten timer. Closed with
estimate-maestro-cli clock-out --at-last-activity <moment>, the interval ends at that moment or at the end of the working day it started in, whichever is earlier. A session that started outside working hours — before 09:00, from 17:00 on, or on a day that is not a working day — has no working day to close against, so it is capped at four hours instead.
So the session started at 22:00 on the Saturday but never ended is considered ended at 02:00 on the Sunday, and one left
running from 17:01 on a Friday closes at 21:01 that evening rather than accruing credit all the way to Monday. Either is
unlikely to be accurate, but both are predictable and bound the damage. Nothing closes a forgotten timer automatically
at the moment; see BACKLOG.md.
An absence recorded over a span subtracts in full from any interval it overlaps.
Tool categories and the taint rule
Every tool the MCP server exposes falls into exactly one category:
recording —
start_effort,add_slice,close_slice,drop_slice,rename_slice,clock_in,clock_out,annotate_interval,repair_interval,record_absence,record_revision,record_final_plan,harvest_commits,record_landed_commit,remap_commits,finish_effort.status —
current, which reports what is clocked in and since when.estimation —
reference_efforts, which shows the estimates recorded against prior efforts, for comparison while a new estimate is being made.calibration —
effort_table,effort_detail,summary, which read the accumulated record back out.
Recording, estimation and status tools never return a duration or an aggregate. Only calibration tools may. This is
enforced structurally: every response model used by a recording, estimation or status tool is checked for field names
containing words such as hours, minutes, duration, wall, average, or share, and the check fails if one is
found.
The reason is narrower than it can look. An estimate made just after seeing the real duration of the work is an anchored echo of that duration rather than an independent judgement; the concrete case to avoid is a final estimate landing on 10 because the clock-out just before it reported ten hours on the effort. So the rule keeps a wall-time figure from surfacing in a tool response while an estimate is being made or a clock is being run. It is a guard against accidental anchoring, not a claim that durations are dangerous — the estimation tools deliberately show prior estimates, which is what makes an abstract scale usable at all. Calibration tools are exempt because reporting exactly those figures is their entire purpose, once the estimating is done.
The estimation and calibration tools are very limited in the initial release. Usage will be needed to refine them into worthwhile tools, but we have to start somewhere. They are a guess at what will be useful, and serve as validation that everything needed is recorded.
Environment variables
All are optional; every one has a default. Read from src/estimate_maestro/config.py, not from this list, if the two
ever disagree.
Variable | Default | Meaning |
|
| Path to the store directory. |
|
| Start of the working day (ISO 8601 time). |
|
| End of the working day (ISO 8601 time). |
|
| Start of the daily lunch break, excluded from working seconds. |
|
| End of the daily lunch break. |
|
| Comma-separated weekdays counted as working days (Monday |
|
| Maximum length given to a forgotten timer that started outside working hours. |
|
| Time added before the earliest commit in a session rebuilt from commits. Unreachable for now; see |
|
| Working-time gap between commits that ends a session rebuilt from commits. Unreachable for now; see |
|
| Minimum number of recorded efforts before |
ESTIMATE_MAESTRO_DERIVE_LEAD_IN_MINUTES must not exceed ESTIMATE_MAESTRO_DERIVE_SESSION_GAP_MINUTES; the server
refuses to start otherwise.
The command-line tool
estimate-maestro-cli covers the everyday actions that do not need a conversation: clock-in, clock-out, current,
record-absence, effort-table, and summary.
Actions that only make sense as part of an estimating conversation — starting an effort, revising an estimate, recording the final plan, harvesting commits — are MCP tools only, not CLI commands, on purpose: they are meant to happen inside a session that shows comparable prior efforts, not as a bare command typed without that context.
Exit codes
Code | Meaning |
| Success. |
| An error occurred. |
|
|
Installation
Register the MCP server:
claude mcp add --scope user maestro-estimate -- uv --directory ~/mcp/estimate-maestro run estimate-maestroThe registered name inverts the directory name on purpose. On disk the server sits among other MCP servers, where
estimate is the half that distinguishes it and the half that sorts usefully; registered with an agent, maestro is
the more interesting half, because there the provenance is what matters — it marks the server as one of mine rather than
something installed from elsewhere.
Symlink the two skills into your personal skills directory:
ln -s ~/mcp/estimate-maestro/skills/recording-work-time ~/.claude/skills/recording-work-time
ln -s ~/mcp/estimate-maestro/skills/estimating-work ~/.claude/skills/estimating-workMake the store a git repository. Its files are plain text and append-only so that it can be committed and roam between machines, and it is the only surviving copy of what it records: a number lost or wrong in there cannot be reconstructed from anywhere else. Keep it private — it holds project names, branch names and how long things took, none of which belong in a public repo.
store="${XDG_DATA_HOME:-$HOME/.local/share}/estimate-maestro"
mkdir -p "$store"
git -C "$store" initThe default store location follows the XDG Base Directory Specification and needs no configuration. Set
ESTIMATE_MAESTRO_STORE only if you want the store somewhere else — for instance alongside your other personal
repositories, so it is easier to remember to commit and push. It must be visible to both the server and your shell, so
put it wherever they both read (typically your shell profile):
export ESTIMATE_MAESTRO_STORE=~/somewhere/elseDevelopment
uv sync --locked
uv run ruff format --check
uv run ruff check
uv run pyright
uv run pytest src/ -vAvailable Tools
21 toolsadd_sliceC
Add a slice. One added while the effort is active is scope growth.
Inputs: title (required), effort (required). Returns: recorded, identifier
annotate_intervalC
Attach meeting participants or a note to an interval after the fact.
Inputs: effort (required), interval (required), note, external, +2 more. Returns: recorded, identifier
clock_inC
Start an interval. Refused if one is already open.
Inputs: effort (required), kind, note, external, +3 more. Returns: recorded, identifier
clock_outC
Close the open interval.
Inputs: at, note. Returns: recorded, identifier
close_sliceC
Close a slice against its pull request.
Inputs: effort (required), work_slice (required), pull_request (required). Returns: recorded, identifier
currentB
What is clocked in, and since when.
Inputs: no named parameters. Returns: since, effort, interval, work_slice
drop_sliceC
Drop a slice that turned out to be unnecessary.
Inputs: effort (required), reason (required), work_slice (required). Returns: recorded, identifier
effort_detailC
One effort's full event history.
Inputs: effort (required). Returns: row, meta, slices, commits, +2 more
effort_tableC
One near-raw row per effort: estimates, wall hours, slice counts.
Inputs: no named parameters. Returns: result
finish_effortC
Mark an effort done.
Inputs: effort (required). Returns: recorded, identifier
harvest_commitsC
Copy commit metadata from a working tree into the store.
Inputs: path (required), since (required), until (required), effort (required), +1 more. Returns: recorded, identifier
record_absenceC
Declare time not spent working. Retroactive by design.
Inputs: kind (required), stop (required), start (required), note. Returns: recorded, identifier
record_final_planD
Record the final plan and the counterfactual estimate against it.
Inputs: effort (required), likely (required), plan_path (required), rationale (required), +3 more. Returns: recorded, identifier
record_landed_commitC
Record the durable commit that landed on the default branch.
Inputs: effort (required), identifier (required), repository (required), work_slice (required). Returns: recorded, identifier
record_revisionC
Record a revised estimate, typically when a slice is inserted.
Inputs: effort (required), likely (required), rationale (required), optimistic (required), +1 more. Returns: recorded, identifier
reference_effortsC
Prior efforts with their summaries and estimates, for comparison. No actuals.
Inputs: kind, limit. Returns: result
remap_commitsC
Map a rewritten commit id onto its replacement.
Inputs: new (required), old (required), effort (required), reason (required). Returns: recorded, identifier
rename_sliceD
Rename a slice.
Inputs: title (required), effort (required), work_slice (required). Returns: recorded, identifier
repair_intervalB
Correct an interval's bounds. Appends; never rewrites.
Inputs: effort (required), interval (required), stop, start. Returns: recorded, identifier
start_effortC
Create an effort from a plan artefact and an initial PERT estimate.
Inputs: kind (required), title (required), likely (required), plan_path (required), +5 more. Returns: recorded, identifier
summaryB
Aggregate calibration, or an honest refusal when there is too little data.
Inputs: no named parameters. Returns: note, efforts_counted, mean_hours_per_point, mean_inventory_growth, +2 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 21 tools
Every tool has a clearly distinct purpose. Even overlapping domains like commits (harvest, record, remap) and intervals (clock, annotate, repair) are separated by specific actions and targets. No two tools could be easily confused for the same operation.
The majority of tools follow a consistent verb_noun pattern (e.g., repair_interval, start_effort, add_slice). A few query tools are named as nouns (current, summary, effort_table, effort_detail), which is a minor deviation but still readable and sensible for retrieval operations.
With 21 tools, the surface is substantial but justified by the domain's complexity—covering efforts, intervals, slices, commits, estimates, and queries. Each tool adds distinct functionality, though the count is on the higher end and could feel heavy for simpler use cases.
The toolset provides comprehensive lifecycle coverage: starting and finishing efforts, managing slices (add/close/drop/rename), tracking time (clock in/out, absence), revising estimates, recording commits, and rich reporting (current, table, detail, summary). No obvious gaps or dead ends are apparent.
Maintenance
Related MCP Connectors
Personal time tracker: start timers, log time and summarize your hours by activity.
Time tracking you talk to: log, check and fix your work hours from your AI assistant.
Save and retrieve your chosen context, manage shared workspaces, and track project time.
Privacy-first work tracking with summaries, reports, coaching, and AI-ready long-term memory.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables time tracking in Tempo with automatic timesheet generation from git commits, worklog management with roles and accounts, and integration with Jira for seamless project time logging.1MIT
- AlicenseAqualityCmaintenanceEnables tracking work time with start/stop timers, logging entries, and generating reports.48 npm28 PyPIMIT
- FlicenseNot gradedqualityBmaintenanceEnables tracking worked hours by client, project, and task, with monthly/daily reports and natural language interaction through Claude.-
- AlicenseNot gradedqualityCmaintenanceEnables local-first time tracking with timers and time entries stored in a SQLite database that sync to Intervals in the background, with local-only reporting and queries.24 npmMIT