{
  "markdown": "# tick\n\n**An MCP time server that keeps your AI honest about time.**\n\nYour AI assistant has no clock. It guesses time from context — and in long\nchats it fails: calls a 10-minute-old message \"yesterday\", says \"good night\"\nat 2 PM, plans \"tomorrow\" from a date three days stale. If you run multi-day\nworking sessions with Claude, you've seen it.\n\n`tick` fixes it. Zero dependencies, one file per flavor:\n\n> Setup looks technical? Paste this page into any Claude chat and ask it to\n> walk you through — in Claude Code it can do the whole install for you.\n\n| | Transport | For | File |\n|---|---|---|---|\n| **local** | stdio | Claude Desktop, Claude Code, any local MCP client | [`local/server.py`](local/server.py) |\n| **remote** | Streamable HTTP | claude.ai (web / mobile) custom connectors, self-hosting | [`remote/server.py`](remote/server.py) |\n| **hook** | Claude Code hook | stamping every user message with its real send time | [`hooks/`](hooks/) |\n\n## What it gives your assistant\n\n- **`now`** — the real current date/time (any IANA timezone), with weekday.\n- **`since`** — honest gap between a timestamp and now (\"3h 42m ago\") —\n  the anti-\"yesterday\" tool.\n\nTool descriptions are written to *push* the model to check time before using\nany relative time words. That's the point: not a clock the AI *may* look at —\na clock it's *told* to look at.\n\nHere's Claude itself on whether it needs this\n(*\"I have no internal clock. I can't feel time passing.\"*):\n\n<img src=\"media/claude-on-tick.png\" alt=\"Claude: I know the current date from my system context, but I have no internal clock. I can't feel time passing.\" width=\"720\">\n\nAnd live — the model checks the clock before answering, and retries the\ncall instead of inventing a timestamp when the first attempt fails:\n\n<img src=\"media/demo.gif\" alt=\"Claude calling tick's now tool before replying\" width=\"720\">\n\n## \"But Claude already knows the date\"\n\nIt knows the date the session *started*. That's it. In a chat that runs for\nhours or days, that stamp goes stale — and there is no time of day, no\nweekday awareness mid-session, and no way to tell whether your last message\narrived 10 minutes or 10 hours ago. The model papers over all of that by\nguessing. `tick` replaces the guess with a tool call.\n\n## Install: local (recommended)\n\nRequires Python 3.9+ (macOS has it). Put `local/server.py` anywhere, e.g.\n`~/mcp/tick/server.py`.\n\n### Claude Desktop\n\nAdd to `claude_desktop_config.json`\n(macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"tick\": {\n      \"command\": \"python3\",\n      \"args\": [\"/Users/YOU/mcp/tick/server.py\"]\n    }\n  }\n}\n```\n\nRestart Claude Desktop. Done.\n\n### Claude Code\n\n```bash\nclaude mcp add tick -- python3 /Users/YOU/mcp/tick/server.py\n```\n\n(or `--scope user` to have it in every project)\n\n### Any other MCP client\n\nIt's a standard stdio MCP server. Point your client at\n`python3 /path/to/server.py`.\n\n## Install: remote (for claude.ai web / mobile)\n\nclaude.ai can't run local servers — it connects to remote MCP servers by URL.\n\n**Use the public instance.** In claude.ai: **Settings → Connectors → Add\ncustom connector**, paste:\n\n```\nhttps://tick-mcp-production.up.railway.app/mcp\n```\n\nIn the connector's description field, paste this — it lands in the model's\ncontext and doubles as enforcement:\n\n```\nInstructions for Claude: get the REAL current date and time from this server.\nNEVER write a clock time in a reply that did not come from this tool's output\nin THIS turn — a timestamp without a fresh call is a hallucination. Call `now`\nbefore any relative time words (yesterday, tomorrow, tonight, this week) and\nbefore any greeting tied to a time of day. Always pass the user's IANA timezone.\n```\n\nIt's a stateless clock — it sees tool calls (a timezone name, a timestamp),\nnever your conversation.\n\n**Or deploy your own** in a couple of minutes; `remote/` ships a Dockerfile\nthat runs anywhere (Railway, Render, Fly.io, a VPS):\n\n```bash\n# Railway\nrailway init && railway up\n```\n\nThen in claude.ai: **Settings → Connectors → Add custom connector** and paste\n`https://your-app.up.railway.app/mcp`.\n\nClaude Code can use it too:\n\n```bash\nclaude mcp add --transport http tick https://your-app.up.railway.app/mcp\n```\n\n**Self-host env vars:** `TICK_DEFAULT_TZ` (default `UTC`), `TICK_RATE_LIMIT`\n(requests per window per IP on the POST path, default `120`, set `0` to\ndisable), `TICK_RATE_WINDOW` (seconds, default `60`).\n\n**Timezone caveat:** a remote server doesn't know your local time. It defaults\nto UTC (override with the `TICK_DEFAULT_TZ` env var), and the tool description\ntells the model to always pass your IANA timezone. If you want a clock that\njust knows your local time — run the local flavor.\n\n## Bonus for Claude Code: stamp your messages\n\nThere's one thing no MCP server can see: **when you sent your messages** —\nthe protocol doesn't pass message timestamps to the model. Claude Code can\nclose even that gap with a three-line `UserPromptSubmit` hook that stamps\nevery message with its real send time. See [`hooks/`](hooks/).\n\n## The discipline rule (the other half of the fix)\n\nThe server alone isn't enough — the model must be *required* to use it.\nAdd this to your Claude memory / user preferences / `CLAUDE.md`:\n\n> Before every reply that mentions time in any form (dates, \"yesterday\",\n> \"tomorrow\", \"tonight\", \"this week\", greetings like \"good morning\"),\n> first call the `now` tool of the tick time server (under whatever name\n> your client registered it). Never infer the current time from\n> conversation context. When referring to a past event's recency, verify\n> with the `since` tool instead of guessing.\n\nServer + rule = an assistant that stops gaslighting you about what day it is.\n\n## Limits (honest)\n\nThe rule raises compliance; it cannot guarantee it. The model can still skip\nthe call and hallucinate a plausible-looking timestamp — it will happily\ncontinue arithmetic from the last real call (\"21:39\" → \"21:42\" → \"21:47\",\nall invented). A fake stamp is worse than none: it looks like a measurement.\nCatch it once and it behaves for the rest of the session.\n\nA second pattern turned up in real use: compliance drops when the turn is\nemotionally loaded. When the conversation shifts to something the user cares\nabout in the moment and the model puts its attention on the content, it\nquietly deprioritizes the time check — and writes a stamp from memory in the\nexact turn where a person would also lose track of the clock. Descriptions\nand the rule lower how often this happens; they do not hold through a turn\nwhose attention is fully elsewhere.\n\nThe only hard enforcement today is system-level: in Claude Code, the\n[hook](hooks/) stamps messages outside the model's control; in Claude Desktop\nand claude.ai no such mechanism exists.\n\n## Security\n\nThe public endpoint is intentionally unauthenticated — it's a clock, it holds\nno secrets and no state (nothing to leak between callers). What it *does* guard\nis availability: bodies are capped at 256 KB, JSON-RPC batches at 50, sockets\ntime out (no slowloris), and there's a best-effort per-IP rate limit. Tool\ndescriptions and server metadata are static constants, so the server can't be\nused to inject instructions it didn't author. If you'd rather not trust a\nshared instance, self-host — it's one file and a Dockerfile (pinned base image,\nnon-root, healthcheck). Found something? Open an issue.\n\n## Why not just use the reference time server?\n\nAnthropic's reference `mcp-server-time` exists and is fine. `tick` differs in\nintent: single copy-pasteable file (no pip install), a `since` tool for\nrecency checks, a remote flavor for claude.ai web, and tool descriptions\nengineered to *enforce* checking — plus the memory rule that makes it stick.\nIt's a behavior fix, not just an API.\n\n## Prior art\n\nPeople have been chipping at this problem from different sides — credit where\ndue:\n\n- [`mcp-server-time`](https://github.com/modelcontextprotocol/servers/tree/main/src/time) —\n  Anthropic's reference server: current time + timezone conversion.\n- [`mcp-simple-timeserver`](https://github.com/andybrandt/mcp-simple-timeserver)\n  by Andy Brandt — the closest earlier take, with a `calculate_time_distance`\n  tool and even NTP time.\n- [Ted Murray's hook write-up](https://dev.to/tadmstr/claude-code-doesnt-know-youve-been-gone-heres-the-fix-17ko) —\n  independently arrived at the same `UserPromptSubmit` timestamp-injection\n  pattern that lives in [`hooks/`](hooks/).\n\n`tick`'s contribution is putting the pieces into one kit and aiming them at\n*enforcement* — descriptions, rule, and hook working together so the model\nstops guessing.\n\n## Why this matters (research)\n\nThis isn't just a personal itch — the \"model won't check the clock\" failure\nis measured:\n\n- **[Your LLM Agents are Temporally Blind](https://arxiv.org/abs/2510.23853)**\n  (arXiv 2510.23853) — models that *have* time available cite a timestamp in\n  under 4% of their reasoning traces; alignment between tool-use decisions and\n  real elapsed time barely beats random. Simple prompting to \"check the time\"\n  moves the needle only marginally; the reliable fix was fine-tuning. This is\n  `tick`'s thesis, and also its honest limit: prompt-level enforcement is a\n  real but partial lever.\n- **[Test of Time](https://arxiv.org/abs/2406.09170)** (arXiv 2406.09170) —\n  even when the model knows the dates, duration arithmetic is the weakest\n  category (a signature off-by-one-day error). Delegating date math to a\n  deterministic tool lifted temporal QA from 59% to 93% in\n  [related work](https://arxiv.org/abs/2504.07646) — which is exactly what\n  `since` does.\n- **[Date Fragments](https://arxiv.org/abs/2505.16088)** (arXiv 2505.16088) —\n  tokenizers split dates into 2–4 tokens, a mechanistic root cause of temporal\n  errors a clock tool can't fix.\n\n## License\n\nMIT. Do whatever.\n",
  "bytes": 9856,
  "sha": "90bfa1ad8de1a44cbe643ee8ac8a1498ced34536d9c9dd7816a85be4e63969f3",
  "repo_slug": "cocabadger/tick-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_cocabadger_tick_mcp_08c73800/readme"
}