Building a Zero-Dependency MCP Server for the Timestamp Bugs LLMs Get Wrong
This is a build log for tsforge-mcp — a pure Node.js MCP server with 16 timestamp/date tools. No npm dependencies, dual transport (stdio + Streamable HTTP). Repo: https://github.com/caresotin/tsforge-mcp
The problem
Most MCP time servers give you get_current_time and convert_time. Fine — but those are also the two things you can just ask the model directly. The real failures show up at the edges:
-
Cron next fire:
0 0 29 2 *(Feb 29) must skip to 2028, not "next year". -
Excel serial 60:
60decodes to1900-02-29— a date that never existed (the famous 1900 leap-year bug). -
ISO-8601 week:
2016-01-01is week 53 of 2015, not week 1. -
DST-aware tz:
Asia/Shanghai → America/New_Yorkis not a fixed offset. -
SQL dialects:
UNIX_TIMESTAMP()vsTO_TIMESTAMP()vsstrftime()vsDATEADD— all different.
Every one of those, asked of a frontier model, returns a confident wrong answer. So I extracted boundary-correct algorithms into tools.
Architecture (no dependencies)
One codebase, two transports:
- stdio for Claude Desktop / local MCP clients.
- Streamable HTTP for ChatGPT Apps SDK / remote clients.
Everything is Node.js built-ins: JSON-RPC 2.0 framing, HTTP session management, and the algorithms in a single module.
// cron_next: handle the 29 Feb edge case
function cronNext(expr, n = 1) {
const sched = parseCron(expr);
const out = [];
let cur = new Date();
while (out.length < n) {
cur = nextMatch(sched, cur);
if (cur) out.push(cur);
cur = new Date(cur.getTime() + 1000);
}
return out;
}
The Excel bug is just a constant offset with a conditional:
function excelSerialToDate(s) {
// Excel wrongly treats 1900 as a leap year; serial 60 = fake Feb 29
const base = new Date(Date.UTC(1899, 11, 30));
const d = new Date(base.getTime() + (s - (s > 60 ? 1 : 0)) * 86400000);
return d;
}
Why this helps the web side
The same engine powers a free converter at https://gotimestamp.com/sql-timestamp-converter.html, and we documented the per-dialect gotchas as language guides:
- MySQL: https://gotimestamp.com/timestamp/mysql
- PostgreSQL: https://gotimestamp.com/timestamp/postgresql
- SQLite: https://gotimestamp.com/timestamp/sqlite
- Node.js: https://gotimestamp.com/timestamp/nodejs
If you're shipping MCP servers, what edge cases have you had to hand-code? Curious whether others hit the same date/time boundary traps.
Top comments (0)