{
  "markdown": "# gnomon-mcp\n\n<!-- mcp-name: io.github.lihtness/gnomon-mcp -->\n\n[![PyPI](https://img.shields.io/pypi/v/gnomon-mcp.svg)](https://pypi.org/project/gnomon-mcp/) [![Python](https://img.shields.io/pypi/pyversions/gnomon-mcp.svg)](https://pypi.org/project/gnomon-mcp/) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![MCP Registry](https://img.shields.io/badge/MCP%20Registry-io.github.lihtness%2Fgnomon--mcp-7c3aed)](https://registry.modelcontextprotocol.io/v0/servers?search=gnomon)\n\n> The pointer on a sundial that turns shadow into time.\n\nA 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.\n\n## Why\n\nLLMs 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.\n\n### When an agent should reach for gnomon\n\nAnywhere the next plausible token is not the right answer. Concretely:\n\n- **Math that matters** — anything beyond trivial mental arithmetic, anything with a decimal point, anything that compounds. Call `calc`.\n- **\"What day is it\" / \"how long until\" / \"how long since\"** — the model's training cutoff is not today. Call `now` for a snapshot; `calendar` with `until`/`since`/`diff` for elapsed time; `parse` for natural-language dates (\"next thursday\").\n- **Date arithmetic across month/year boundaries** — adding 30 days, finding a quarter-end, counting business days. Models routinely off-by-one these. Call `calendar` with `add` / `business_days`.\n- **Unit conversion** — call `calc_convert`. Never eyeball \"kg → lb\" or \"°C → °F\".\n- **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.\n\nThe rule of thumb: if you'd ask a colleague to \"just double-check that number,\" call gnomon instead.\n\n### How this compares to other MCP servers\n\nTime and math already have several MCP servers — the official [`Time`](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/time) reference (timezone-only), [`mcp-time`](https://github.com/TheoBrigitte/mcp-time) and [`mcp-datetime`](https://github.com/ZeparHyfar/mcp-datetime) (date formatting / timezone), [`calculator-server`](https://github.com/avisangle/calculator-server) (math + units, no dates), and bundles like [`agent-utils-mcp`](https://github.com/aparajithn/agent-utils-mcp) (regex / hashing / JWT). gnomon's lane is narrower:\n\n- **Batch-first.** `calendar(ops)` and `calc(expressions)` take lists; one tool call covers a whole table column instead of N calls.\n- **A real `now()`.** One call returns 18 fields — ISO week, quarter, fiscal year, day-of-year, `is_weekend`, … — instead of just `{iso, tz}`.\n- **Dates *and* math *and* units in one wiring.** No need to compose three separate servers.\n- **Natural-language dates baked in** (`\"next thursday\"`, `\"in 3 hours\"`) without a separate NLP server.\n\nIf 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.\n\n## Tools\n\n### Calendar\n\nTwo tools:\n\n- **`now(tz?)`** — standalone. Returns a rich dict snapshot of the current moment. One call gets you everything about \"right now\".\n- **`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).\n\n**`now(tz?)`** returns:\n\n```python\n{\n  \"iso\": \"2026-05-25T14:30:45+00:00\",\n  \"date\": \"2026-05-25\",\n  \"time\": \"14:30:45\",\n  \"unix\": 1779345045,\n  \"tz\": \"UTC\",\n  \"year\": 2026, \"month\": 5, \"month_name\": \"May\", \"day\": 25,\n  \"weekday\": \"Monday\", \"weekday_num\": 0,          # 0=Monday\n  \"day_of_year\": 145, \"week_of_year\": 22,         # ISO week\n  \"quarter\": 2, \"fiscal_year_us_gov\": 2026,       # FY starts Oct 1\n  \"hour\": 14, \"minute\": 30, \"second\": 45,\n  \"is_weekend\": False,\n}\n```\n\n**`calendar(ops)`** operations:\n\n| Op | Params | Returns |\n|---|---|---|\n| `diff` | `start, end, unit` | `end - start` — time elapsed between two known dates |\n| `until` | `target, unit, tz?` | `target - now` — time left to a future point (negative if past) |\n| `since` | `source, unit, tz?` | `now - source` — time elapsed since a past point (negative if future) |\n| `add` | `date, n, unit` | ISO of `date + n units` (`seconds\\|...\\|weeks`, plus `months\\|years` calendar-aware) |\n| `weekday` | `date` | `\"Monday\"`..`\"Sunday\"` |\n| `business_days` | `start, end` | count of Mon-Fri days (start inclusive, end exclusive) |\n| `parse` | `natural, tz?` | ISO from natural language (\"next thursday\", \"in 3 hours\") |\n| `format` | `date, fmt` | strftime-formatted string |\n\nUnits for `diff`/`until`/`since`: `seconds`, `minutes`, `hours`, `days`, `weeks`.\n\nExample — compute several things in one call:\n\n```python\ncalendar([\n  {\"op\": \"until\", \"target\": \"2026-12-31\", \"unit\": \"days\"},          # days left in year\n  {\"op\": \"since\", \"source\": \"2026-01-01\", \"unit\": \"days\"},          # days elapsed in year\n  {\"op\": \"diff\", \"start\": \"2026-01-01\", \"end\": \"2026-12-31\", \"unit\": \"days\"},\n  {\"op\": \"weekday\", \"date\": \"2026-05-25\"},                           # \"Monday\"\n  {\"op\": \"add\", \"date\": \"2026-05-25\", \"n\": 1, \"unit\": \"months\"},\n  {\"op\": \"parse\", \"natural\": \"next thursday\", \"tz\": \"America/Los_Angeles\"},\n])\n```\n\n### Calculator\n\n| Tool | Purpose |\n|---|---|\n| `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. |\n| `calc_convert(value, from_unit, to_unit)` | Unit conversion via Pint (`meter` → `foot`, `kg` → `lb`, `degC` → `degF`, etc.). |\n\nExamples:\n\n```python\ncalc([\"2 + 3 * 4\"])                  # [14]\ncalc([\"sqrt(16)\", \"sin(pi/2)\"])      # [4.0, 1.0]\ncalc([\"mean([1, 2, 3, 4])\"])         # [2.5]\ncalc([\"sum(range(101))\"])            # [5050]\ncalc([\"(25 / 100) * 100\"])           # [25.0]\n```\n\n## Future tools (sketches)\n\nThe 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:\n\n1. **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.\"\n2. **Regex match / replace** — `regex_find(pattern, text)` and `regex_sub(pattern, repl, text)`. Models hallucinate which substrings match a regex; a real engine ends the argument.\n3. **Structured-data extraction** — `jq(path, json)` / `jsonpath(path, json)`. Reading values out of a nested blob by path, without typos.\n4. **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.\n5. **Decimal money math** — `money(expr)` evaluated under Python's `Decimal` with explicit rounding. `calc` is float-based and quietly unsafe for currency.\n6. **Holiday-aware business days** — extend `calendar.business_days` with a `country` (or `calendar`) parameter so US/UK/IN holidays are excluded. The current implementation only knows weekends.\n7. **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.\n8. **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.\n\nIf you want one of these, open an issue (or a PR — each is a small self-contained module that fits the existing `tools/` layout).\n\n## Install\n\nRecommended: no install — run on demand via [`uv`](https://docs.astral.sh/uv/):\n\n```bash\nuvx gnomon-mcp           # serves stdio MCP, ready for any client\nuvx gnomon-mcp --demo    # call every tool once and print the results (no MCP client needed)\n```\n\nOr install globally:\n\n```bash\npip install gnomon-mcp\n```\n\n## Wire it into your agent\n\nAll recipes assume `uvx gnomon-mcp`. If you prefer a pinned install, swap the command for `gnomon-mcp` (with no `uvx`).\n\n### Claude Code\n\n```bash\nclaude mcp add gnomon -- uvx gnomon-mcp\n```\n\nOr edit `~/.claude.json` / a project `.mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"gnomon\": { \"command\": \"uvx\", \"args\": [\"gnomon-mcp\"] }\n  }\n}\n```\n\n### Claude Desktop\n\n`claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"gnomon\": { \"command\": \"uvx\", \"args\": [\"gnomon-mcp\"] }\n  }\n}\n```\n\n### Cursor\n\n`~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project):\n\n```json\n{\n  \"mcpServers\": {\n    \"gnomon\": { \"command\": \"uvx\", \"args\": [\"gnomon-mcp\"] }\n  }\n}\n```\n\n### Continue\n\n`~/.continue/config.yaml`:\n\n```yaml\nmcpServers:\n  - name: gnomon\n    command: uvx\n    args: [\"gnomon-mcp\"]\n```\n\n### Any other client (generic stdio)\n\nSpawn `uvx gnomon-mcp` as a subprocess and speak MCP over stdin/stdout. That is the entire integration.\n\n### Hosted / remote (HTTP transport)\n\nFor team-shared instances or agents that can't spawn a local subprocess:\n\n```bash\nuvx gnomon-mcp --transport streamable-http --host 0.0.0.0 --port 8000\n# also supported: --transport sse\n```\n\nThen point your MCP client at `http://<host>:8000/mcp` (or `/sse` for the SSE transport).\n\n## Tell your agent to actually use it\n\nThe MCP tool descriptions are intentionally terse to keep persistent context cost minimal (~150 tokens for all four tools). The richer \"when to reach for gnomon\" guidance lives in a Claude Code skill that loads on demand.\n\n### Option A — Claude Code plugin (one command, recommended)\n\nThe plugin wires both the MCP server *and* the skill in one shot. Inside Claude Code:\n\n```\n/plugin marketplace add lihtness/gnomon-mcp\n/plugin install gnomon@gnomon-mcp\n```\n\nThat registers `gnomon` as an MCP server (auto-starts via `uvx`) and installs the on-demand skill. Skill body loads only when the task triggers it — persistent context stays ~150 tokens for the four tool descriptions plus ~40 tokens for the skill's name + summary.\n\n### Option B — manual skill install (no plugin)\n\nIf you've already wired the MCP server with `claude mcp add gnomon -- uvx gnomon-mcp` and only want the skill:\n\n```bash\nmkdir -p ~/.claude/skills/gnomon\ncurl -fsSL https://raw.githubusercontent.com/lihtness/gnomon-mcp/main/skills/gnomon/SKILL.md \\\n  -o ~/.claude/skills/gnomon/SKILL.md\n```\n\n### Option C — paste into your system prompt (non-Claude-Code agents)\n\nFor agents without skill support, paste this short version into your system prompt or `CLAUDE.md`:\n\n```text\nYou have gnomon: deterministic tools for dates and math. Use them instead\nof guessing.\n\n- `now` — current moment (your training cutoff isn't today).\n- `calendar(ops)` — batch date math: diff/until/since/add/weekday/\n  business_days/parse (natural language)/format.\n- `calc(expressions)` — batch Python eval; math + statistics + common\n  builtins pre-loaded.\n- `calc_convert(value, from, to)` — unit conversion via Pint.\n\nBoth batch tools take a list and return a list. Prefer one batched call\nover many small ones.\n```\n\n## Development\n\n```bash\ngit clone https://github.com/lihtness/gnomon-mcp\ncd gnomon-mcp\npip install -e \".[dev]\"\npytest\n```\n\n## License\n\nMIT\n",
  "bytes": 11786,
  "sha": "99f98d61748771cfd9ee0f49c0d196481895c37c27e104d155c1c446cb6ec06d",
  "repo_slug": "lihtness/gnomon-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_lihtness_gnomon_mcp_670e12ce/readme"
}