A small MCP server for the boring-but-essential utilities: dates, calendars, arithmetic, unit conversion.
Project description
gnomon-mcp
The pointer on a sundial that turns shadow into time.
A small MCP server for the boring-but-essential utilities every model needs: dates, calendars, arithmetic, unit conversion. Use it so your assistant stops "next-token guessing" math and date math.
Why
LLMs are bad at arithmetic and date math by default. They produce plausible answers that are often wrong by a small amount — exactly the kind of mistake that's hard to notice in a long response. gnomon-mcp exposes deterministic Python implementations through MCP so your model can compute instead of guess.
When an agent should reach for gnomon
Anywhere the next plausible token is not the right answer. Concretely:
- Math that matters — anything beyond trivial mental arithmetic, anything with a decimal point, anything that compounds. Call
calc. - "What day is it" / "how long until" / "how long since" — the model's training cutoff is not today. Call
nowfor a snapshot;calendarwithuntil/since/difffor elapsed time;parsefor natural-language dates ("next thursday"). - Date arithmetic across month/year boundaries — adding 30 days, finding a quarter-end, counting business days. Models routinely off-by-one these. Call
calendarwithadd/business_days. - Unit conversion — call
calc_convert. Never eyeball "kg → lb" or "°C → °F". - Table-row workloads — when the same kind of computation needs to run on every row of a table, both batch tools (
calendar,calc) take a list and return a list in order. One call, N results.
The rule of thumb: if you'd ask a colleague to "just double-check that number," call gnomon instead.
How this compares to other MCP servers
Time and math already have several MCP servers — the official Time reference (timezone-only), mcp-time and mcp-datetime (date formatting / timezone), calculator-server (math + units, no dates), and bundles like agent-utils-mcp (regex / hashing / JWT). gnomon's lane is narrower:
- Batch-first.
calendar(ops)andcalc(expressions)take lists; one tool call covers a whole table column instead of N calls. - A real
now(). One call returns 18 fields — ISO week, quarter, fiscal year, day-of-year,is_weekend, … — instead of just{iso, tz}. - Dates and math and units in one wiring. No need to compose three separate servers.
- Natural-language dates baked in (
"next thursday","in 3 hours") without a separate NLP server.
If you only need timezone conversion, the official Time server is enough. If you want a broad utility bundle (regex, hashing, encoding, JWT), agent-utils-mcp is a better fit. gnomon is for the boring date-arithmetic-and-arithmetic core, batched.
Tools
Calendar
Two tools:
now(tz?)— standalone. Returns a rich dict snapshot of the current moment. One call gets you everything about "right now".calendar(ops)— batch dispatcher. Each item picks its own op. Designed for table-row workloads (e.g. one call computes time-elapsed for every row).
now(tz?) returns:
{
"iso": "2026-05-25T14:30:45+00:00",
"date": "2026-05-25",
"time": "14:30:45",
"unix": 1779345045,
"tz": "UTC",
"year": 2026, "month": 5, "month_name": "May", "day": 25,
"weekday": "Monday", "weekday_num": 0, # 0=Monday
"day_of_year": 145, "week_of_year": 22, # ISO week
"quarter": 2, "fiscal_year_us_gov": 2026, # FY starts Oct 1
"hour": 14, "minute": 30, "second": 45,
"is_weekend": False,
}
calendar(ops) operations:
| Op | Params | Returns |
|---|---|---|
diff |
start, end, unit |
end - start — time elapsed between two known dates |
until |
target, unit, tz? |
target - now — time left to a future point (negative if past) |
since |
source, unit, tz? |
now - source — time elapsed since a past point (negative if future) |
add |
date, n, unit |
ISO of date + n units (seconds|...|weeks, plus months|years calendar-aware) |
weekday |
date |
"Monday".."Sunday" |
business_days |
start, end |
count of Mon-Fri days (start inclusive, end exclusive) |
parse |
natural, tz? |
ISO from natural language ("next thursday", "in 3 hours") |
format |
date, fmt |
strftime-formatted string |
Units for diff/until/since: seconds, minutes, hours, days, weeks.
Example — compute several things in one call:
calendar([
{"op": "until", "target": "2026-12-31", "unit": "days"}, # days left in year
{"op": "since", "source": "2026-01-01", "unit": "days"}, # days elapsed in year
{"op": "diff", "start": "2026-01-01", "end": "2026-12-31", "unit": "days"},
{"op": "weekday", "date": "2026-05-25"}, # "Monday"
{"op": "add", "date": "2026-05-25", "n": 1, "unit": "months"},
{"op": "parse", "natural": "next thursday", "tz": "America/Los_Angeles"},
])
Calculator
| Tool | Purpose |
|---|---|
calc(expressions) |
Evaluate a list of Python expressions and return a list of results. Math (sqrt, sin, log, pi, e, ...), stats (mean, median, stdev, variance), and useful builtins (abs, round, min, max, sum, range, sorted, ...) are pre-loaded. Batch in / batch out, order preserved. |
calc_convert(value, from_unit, to_unit) |
Unit conversion via Pint (meter → foot, kg → lb, degC → degF, etc.). |
Examples:
calc(["2 + 3 * 4"]) # [14]
calc(["sqrt(16)", "sin(pi/2)"]) # [4.0, 1.0]
calc(["mean([1, 2, 3, 4])"]) # [2.5]
calc(["sum(range(101))"]) # [5050]
calc(["(25 / 100) * 100"]) # [25.0]
Future tools (sketches)
The same logic — if the model is likely to bluff it, expose a deterministic version — points at several more primitives worth building. None of these are implemented yet; they are candidates, listed roughly in order of bang-for-buck:
- Text measurement —
count(text, unit)for chars / words / lines / sentences / LLM tokens. Agents constantly miscount "how long is this" and "will this fit in the context window." - Regex match / replace —
regex_find(pattern, text)andregex_sub(pattern, repl, text). Models hallucinate which substrings match a regex; a real engine ends the argument. - Structured-data extraction —
jq(path, json)/jsonpath(path, json). Reading values out of a nested blob by path, without typos. - Hashing & encoding —
hash(text, algo)(sha256, md5, blake2),encode(text, scheme)/decode(text, scheme)(base64, hex, url, jwt-payload). All things models confidently invent wrong. - Decimal money math —
money(expr)evaluated under Python'sDecimalwith explicit rounding.calcis float-based and quietly unsafe for currency. - Holiday-aware business days — extend
calendar.business_dayswith acountry(orcalendar) parameter so US/UK/IN holidays are excluded. The current implementation only knows weekends. - Cron describe / next-fire —
cron_describe("0 9 * * 1-5")→ human English;cron_next(expr, n)→ next N firing times. Models routinely misread cron fields. - Token counting for a target model —
count_tokens(text, model)via tiktoken / Anthropic tokenizer. Lets an agent budget its own prompts and outputs instead of guessing.
If you want one of these, open an issue (or a PR — each is a small self-contained module that fits the existing tools/ layout).
Install
Recommended: no install — run on demand via uv:
uvx gnomon-mcp # serves stdio MCP, ready for any client
uvx gnomon-mcp --demo # call every tool once and print the results (no MCP client needed)
Or install globally:
pip install gnomon-mcp
Wire it into your agent
All recipes assume uvx gnomon-mcp. If you prefer a pinned install, swap the command for gnomon-mcp (with no uvx).
Claude Code
claude mcp add gnomon -- uvx gnomon-mcp
Or edit ~/.claude.json / a project .mcp.json:
{
"mcpServers": {
"gnomon": { "command": "uvx", "args": ["gnomon-mcp"] }
}
}
Claude Desktop
claude_desktop_config.json:
{
"mcpServers": {
"gnomon": { "command": "uvx", "args": ["gnomon-mcp"] }
}
}
Cursor
~/.cursor/mcp.json (or .cursor/mcp.json in a project):
{
"mcpServers": {
"gnomon": { "command": "uvx", "args": ["gnomon-mcp"] }
}
}
Continue
~/.continue/config.yaml:
mcpServers:
- name: gnomon
command: uvx
args: ["gnomon-mcp"]
Any other client (generic stdio)
Spawn uvx gnomon-mcp as a subprocess and speak MCP over stdin/stdout. That is the entire integration.
Hosted / remote (HTTP transport)
For team-shared instances or agents that can't spawn a local subprocess:
uvx gnomon-mcp --transport streamable-http --host 0.0.0.0 --port 8000
# also supported: --transport sse
Then point your MCP client at http://<host>:8000/mcp (or /sse for the SSE transport).
Tell your agent to actually use it
The single biggest reason agents still guess after you wire in a tool is that they were never told to reach for it. Paste this (or a variant) into your agent's system prompt:
You have access to gnomon, a deterministic MCP tool server for dates and math.
Use it instead of computing in your head — your arithmetic and date estimates
are unreliable, gnomon's are not.
- `now` (optional tz): the current moment. Call this whenever a request
depends on "today", "right now", "this week", etc. Your training cutoff
is not today.
- `calendar(ops)`: batch dispatcher for date math. Use for time-until,
time-since, elapsed time, weekday lookup, business-day counts, adding
months/years to dates, formatting, and natural-language date parsing
("next thursday", "in 3 hours").
- `calc(expressions)`: batch Python expression evaluator with math, stats,
and common builtins pre-loaded. Use for any arithmetic past trivial
mental math, including percentages, sums, averages, and table-row math.
- `calc_convert(value, from_unit, to_unit)`: unit conversion (meters/feet,
kg/lb, °C/°F, bytes/MiB, etc.). Never eyeball these.
Both batch tools take a list and return a list in order — prefer one batched
call over many small ones.
Development
git clone https://github.com/lihtness/gnomon-mcp
cd gnomon-mcp
pip install -e ".[dev]"
pytest
License
MIT
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 gnomon_mcp-0.1.0.tar.gz.
File metadata
- Download URL: gnomon_mcp-0.1.0.tar.gz
- Upload date:
- Size: 13.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":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 |
96616e93387294cf5d79011cf5bb6abdd112be7c6248b80d73791cf7edbefac7
|
|
| MD5 |
1ec00af5de2ab400a97a1cb61d56fc5b
|
|
| BLAKE2b-256 |
0737f193e4d125d94f688be262734525d043792f67639dbb121cc15ca41c4738
|
File details
Details for the file gnomon_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: gnomon_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":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 |
b1e87f045cf374f6e8964b48cabf4bc448f36b9cd45aae5a3a4e40da5e91dd01
|
|
| MD5 |
1318c753ee18b3cd2226d2dfbd198672
|
|
| BLAKE2b-256 |
c0eecb221f5cbcdd59b6749be5cba0b3a85c9dbe753e4a028a999f2c605eff73
|