Cross-agent time-calibration corpus served over MCP
Project description
TimeCal
A cross-agent time-calibration corpus, served over MCP. It counters the systematic over-estimation that LLM agents inherit from ~30 years of human software-engineering timelines.
Problem
When an agent reads "build a Reddit→Claude pipeline," its training prior maps that to engineer-weeks. A capable agent driving Claude Code can ship the same thing in an afternoon. That bad prior cascades: things get called "infeasible solo," scope gets cut that didn't need cutting, multi-phase rollouts get proposed where one session would do.
Asserting "you're powerful" doesn't override the prior — examples do.
Solution
A small MCP server backed by a local SQLite corpus of real "human-estimated → actually-took" rows. Any MCP-aware agent (Claude Code, Codex, Cursor, Cline) can:
- Call
calibrate_task(task_description)to retrieve similar past rows before scoping. - Call
log_completion(...)to append new rows as work finishes, so the corpus grows. - Load the
timecal://preambleresource to reset the prior at conversation start.
The corpus separates two clocks (wall-clock days vs. active hours — different units, never compared raw) and tags every row with a regime so the reading agent can tell whether a human "months" estimate was a fake prior or a real external constraint.
Quickstart
Zero-install, with uv:
uvx timecal # runs the MCP server over stdio
Or install it:
pip install timecal
python -m timecal.server
The corpus DB is created and seeded automatically on first run (10-row example corpus), at ~/.timecal/timecal.db. No setup step. Point TIMECAL_DB at any path to use your own corpus instead — the server, scripts, and tests all read it at call time.
From source (for development):
git clone https://github.com/Conalh/timecal && cd timecal
pip install -e ".[dev]"
pytest -q # 25 passing
Use it from an agent
Claude Code:
claude mcp add timecal -- uvx timecal
Generic MCP client (mcp.json / client config):
{
"mcpServers": {
"timecal": {
"command": "uvx",
"args": ["timecal"],
"env": { "TIMECAL_DB": "/path/to/your/corpus.db" }
}
}
}
(env is optional — drop it to use the auto-seeded default.) Once connected, the agent gets two tools (calibrate_task, log_completion) and one resource (timecal://preamble).
What it changes — before / after
Same task, same model, the difference is one calibrate_task call:
User: Can we build a Slack bot that summarizes standup messages?
Without TimeCal: "That's a solid 2–3 week project — you'll need to design the webhook integration, build the summarization pipeline, handle Slack's API rate limits, and test across channels. I'd suggest phasing it: week 1 plumbing, week 2 the LLM layer, week 3 hardening."
With TimeCal (after
calibrate_task("slack bot that summarizes standup messages")returns thestandup-summary-botrow — human bucket: weeks, actual: ~5h active, agent_executed): "A close match in the corpus shipped in about 5 active hours, agent-executed — the webhook plumbing dominated, not the LLM call. This is an afternoon, not a sprint. Want me to start on the webhook handler?"
Layout
timecal/
README.md this file
LICENSE MIT
pyproject.toml packaging + pytest/ruff config
src/timecal/
server.py MCP server entrypoint (FastMCP, stdio)
calibrate.py retrieval / ranking + agent-facing formatting
log.py validation + insert (mcp-free, unit-tested)
db.py DB path + auto-init/seed (honors TIMECAL_DB)
data/
schema.sql SQLite schema (shipped in the wheel)
example.csv 10-row synthetic corpus, auto-seeded on first run
scripts/
bootstrap.py pre-create + seed the DB without starting the server
init_db.py create an empty DB from schema
import_seed.py import a reviewed CSV (validates enums)
tests/ pytest suite (db, calibrate, log, import_seed)
Data model
Every row in projects carries:
regime— what kind of work it was; drives whether a human estimate was a fake prior or a real constraint:agent_executed— agent does the work end-to-end; human-week estimates are usually agent-hour.review_bound— agent produces code in minutes, but human review / re-prompting dominates wall-clock.external_bound— gated by people, data accrual, or training runs; "months" is months, not a prior.
- two clocks —
wall_clock_days(includes idle gaps) andactive_hours(real work time). Different units; the corpus never compares them raw. estimate_bucket— ordinal of what a human team would have estimated (hours…year_plus), plusestimate_rawfor nuance.data_quality— how the row was measured:dates_only,timed_session, orself_reported.source— provenance. Rows with an emptysourceare filtered out of the defaultcalibrate_taskresponse, so synthetic exploration data can't pollute the agent's view.
Bring your own corpus
The auto-seeded example rows (marked source=example) are synthetic — enough to make the demo runnable, not enough to be your real prior. Build your own:
- point
TIMECAL_DBat a fresh path, then log tasks as you finish them via thelog_completiontool, or - import a reviewed CSV into your
TIMECAL_DB:python scripts/import_seed.py path/to/your.csv(rejects, rather than silently drops, rows with bad enums or emptywhat_shipped).
To keep your corpus free of the example rows, delete them with DELETE FROM projects WHERE source = 'example'; or start from an empty DB via python scripts/init_db.py.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file timecal-0.1.0.tar.gz.
File metadata
- Download URL: timecal-0.1.0.tar.gz
- Upload date:
- Size: 17.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd8c5c890cbfac2c79b421d3d4f929cea26d7d4b8731de65bd62442f8ee2409c
|
|
| MD5 |
cd3f30cf9a52adf29c2210a24a1c610d
|
|
| BLAKE2b-256 |
b8b01bb34a04fb907ee125e9737de1df4e6354e36946cda8cf565b893828a470
|
File details
Details for the file timecal-0.1.0-py3-none-any.whl.
File metadata
- Download URL: timecal-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da9256d7c7ed6fe229841e8b0448fa1a313e01be72a9f6b886536c55175d56aa
|
|
| MD5 |
5d56a936a2bebb356cc9a55b3f0f58e3
|
|
| BLAKE2b-256 |
d80a075c6d8e59ccf623803cf57d784a45ae0d36fcf721d0604833cb5aa00622
|