io.github.theoddden/stamp
This MCP server provides clock/time monitoring (NTP time and drift analysis), UUID generation, text diffing, safe arithmetic, and tamper-evident attestation/verification.
get_time: Query an NTP server for current UTC time, local clock offset (ms), network delay, and stratum; appends each sample to a local drift log.
get_drift: Analyze the drift log to report sample count, timespan, current/mean/stddev offset, drift rate (ms/day), trend verdict (stable, drifting, accelerating), and optional server filtering.
generate_uuid: Generate random UUIDv4 identifiers, optionally up to 1000 at once.
diff: Produce a unified diff between two text inputs, with added/removed line counts and an identical flag; optional context lines.
calculate: Evaluate mathematical expressions safely (arithmetic, parentheses, functions, constants) via AST parsing — no eval or arbitrary code.
attest: Wrap any JSON payload in a tamper-evident record containing a UUID, timestamp (NTP-verified or local, with time_source), and a SHA-256 hash.
verify: Recompute the hash of an attested record and report validity, detecting any modification to payload, timestamp, or id.
Allows querying Cloudflare's public NTP server (time.cloudflare.com) to retrieve current UTC time, clock offset, network delay, and stratum, and to track clock drift over time.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@io.github.theoddden/stampwhat's the current UTC time and how much is my clock drifting?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Stamp
The concierge MCP for agentic workflows: the small, high-frequency tools agents reach for constantly -- NTP time and clock drift, UUIDs, diffs, safe arithmetic, and tamper-evident attestation -- behind one endpoint.
A single NTP query tells you where your clock is right now. Drift history
tells you where it is going: a clock that is consistently 200 ms fast and
accelerating is a different problem from one that is stable at 200 ms
fast. get_time takes the measurement; every call appends to a local
log; get_drift reads the log and reports the trend.
Hosted endpoint (no sign-in, public):
https://stamp-mcp.terradev.cloud/mcp
PyPI: pip install stamp-mcp | License: Apache 2.0
Quick start
Remote (streamable HTTP — no install needed)
Add to your MCP client config:
{
"mcpServers": {
"stamp": {
"url": "https://stamp-mcp.terradev.cloud/mcp"
}
}
}Local (stdio)
pip install stamp-mcpClaude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"stamp": {
"command": "stamp-mcp"
}
}
}Restart Claude Desktop, then ask it "what tools do you have?" — get_time
should appear. If it doesn't, check ~/Library/Logs/Claude/mcp*.log.
Related MCP server: chuk-mcp-time
Tools
get_time— one NTP query: UTC time, this clock's offset in ms, network delay, stratum. Appends the sample to the drift log. Optional:server(defaulttime.cloudflare.com),timezone(IANA name for alocalfield, e.g.America/New_York).get_drift— analyzes the drift log: sample count, timespan, current/mean/stddev offset, drift rate in ms/day (least-squares fit), first-half vs. second-half rates, and a verdict:stable,drifting, oraccelerating. Optional:serverto filter by NTP host.generate_uuid— random UUIDv4s for records, sessions, and identifiers. Optional:count(1–1000, default 1).diff— unified diff betweentext_aandtext_b, with added/removed line counts and anidenticalflag. Optional:context(lines of context, default 3).calculate— safe math evaluator:+ - * / // % **, parentheses, functions (abs round min max sqrt floor ceil exp log log2 log10 pow sin cos tan), constantspi e tau inf. Parsed to an AST; only whitelisted nodes are evaluated — noeval(), no arbitrary code. Required:expression.attest— wrap any JSON payload in a tamper-evident record: a UUID, an NTP-verified timestamp (falls back to local clock;time_sourcesays which), and asha256over the canonical record. Required:payload. Optional:server.verify— recompute an attested record's hash and compare. Returnsvalidplus a reason; any modified field — payload, timestamp, id — breaks it. Required:attested.
The drift log
Every get_time call appends one JSON line to ~/.stamp/drift.jsonl
(override with STAMP_DRIFT_LOG). The file is capped at 10,000 samples.
Call get_time periodically — a cron job, a heartbeat, or just asking
Claude "check the clock" — and get_drift turns the accumulated offsets
into a trend.
HTTP transport
stamp_mcp/http_server.py serves the same dispatch logic over async HTTP
via aiohttp:
stamp-mcp-http # binds 127.0.0.1:8000
STAMP_PORT=9000 stamp-mcp-http # custom portEndpoint | Method | Description |
|
| JSON-RPC 2.0 — single or batch; notifications → 202 |
|
| SSE channel for server-to-client notifications (keep-alive) |
|
| Service identity: name, version, endpoint map |
|
|
|
|
| RFC 9728 metadata — public, no auth servers |
|
| A2A agent card |
TLS is terminated by a reverse proxy. The production layout is two
containers on one AWS instance (docker-compose.yml):
stamp— aiohttp server built fromDockerfile, internal network only. Drift log persists in thestamp-datavolume.caddy— terminates HTTPS atstamp-mcp.terradev.cloud(automatic Let's Encrypt) and reverse-proxies tostamp:8000.
Concurrency is capped at 100 simultaneous POST /mcp requests via
asyncio.Semaphore; /health and / are exempt.
Self-hosting
git clone https://github.com/theoddden/Stamp-MCP.git
cd Stamp-MCP
docker compose up -d --buildCaddy handles TLS automatically once your DNS A record points at the host.
See deploy/Caddyfile and deploy/stamp-mcp.service (systemd, bare-metal
alternative).
Deployment to AWS is automated via GitHub Actions (deploy.yml) using SSM
Run Command — no inbound SSH needed.
Wire protocol
>>> {"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}
<<< {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05",...}}
>>> {"jsonrpc":"2.0","method":"notifications/initialized"}
>>> {"jsonrpc":"2.0","id":2,"method":"tools/list"}
<<< {"jsonrpc":"2.0","id":2,"result":{"tools":[...]}}
>>> {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_time","arguments":{}}}
<<< {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"..."}]}}License
Copyright 2024 theoddden. Licensed under the Apache License, Version 2.0.
DISCLAIMER: Stamp queries public NTP infrastructure (Cloudflare, stratum 3) and is suitable for general agentic workflows. It is not intended for use cases requiring certified atomic precision, legal timestamp authority, or regulated audit trails. Use in production systems is at the implementer's risk.
Available Tools
2 toolsget_driftAInspect
Analyze the drift log built by get_time: reports current offset, drift rate (ms/day), and whether the clock is stable, drifting, or accelerating.
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | Only analyze samples from this NTP server |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the full burden. It indicates an analysis operation ('analyze') and lists output fields, but does not explicitly state whether the tool is read-only, whether it requires prior calls to get_time, or what happens if the log is empty or missing. This lack of behavioral disclosure for a tool with no annotations is a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, concise sentence that front-loads the purpose and directly states the tool's outputs. There is no redundant or unnecessary information, and it is well-structured for quick comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the tool's purpose and output fields (offset, drift rate, status), which is adequate for a simple tool with one optional parameter. It does not mention edge cases like empty logs or error conditions, but given the simplicity and the reference to get_time as the data source, it is reasonably complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has full 100% coverage for the single optional parameter 'server' with a descriptive comment. The description does not add any extra meaning about the parameter beyond what the schema already states, so it meets the baseline of 3 for high schema coverage without adding further value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: analyzing the drift log produced by get_time, and explicitly lists the reported outputs (offset, drift rate, stability status). This is a specific verb+resource definition that distinguishes it from the sibling get_time by referencing it as the source of the log.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context by mentioning that the drift log is 'built by get_time', implying this tool is used after get_time has been called. While it doesn't explicitly state exclusions or alternatives, the reference to the prerequisite makes the usage context understandable for the single sibling scenario.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_timeAInspect
Query an NTP server for current UTC time and this clock's offset, and append the sample to the drift log. Call it periodically to build drift history.
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | NTP server to query | time.cloudflare.com |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and it discloses an important behavioral trait: the call appends a sample to the drift log, which is a side effect beyond a simple read. It does not go into failure modes or output format, but it covers the main behavioral surprise.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences: the first states the operation and side effect, the second gives usage guidance. Every sentence earns its place, and there is no redundant wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one optional parameter and no output schema, the description covers what it does, what side effect it causes, and how often to call it. It does not explicitly connect to the sibling get_drift, but the core calling context is sufficiently complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for the single parameter, so the schema already documents 'server' as the NTP server to query. The description adds almost no semantic value beyond the schema, making the baseline 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description is specific: 'Query an NTP server for current UTC time and this clock's offset, and append the sample to the drift log.' It names a clear action, resource, and side effect, and it implicitly contrasts with the sibling get_drift by describing a data-collection operation rather than a read operation, though it never explicitly names the sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The instruction 'Call it periodically to build drift history' gives clear context for when the tool should be used. However, it does not mention alternatives or explicitly say when not to use it compared to get_drift.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v0.3.0- First observed
get_drift - First observed
get_time
TDQS
Scored across 2 tools
get_time is responsible for sampling a new NTP timestamp and appending it to the drift log, while get_drift is responsible for analyzing the accumulated log. Although both mention offset, their roles are clearly separated: one collects data, the other interprets it.
Both tools follow a consistent get_<noun> naming pattern, making the tool surface predictable and easy to understand. There is no mixing of styles or vague verbs.
With only two tools, the server is on the thin side, but the two tools cover the core sampling and analysis workflow of a narrow domain. The count is understandable but still feels minimal.
The server covers the essential workflow: sample time and analyze drift. Minor gaps exist, such as no way to inspect raw log entries or clear/reset the drift log, but these are not fatal for the stated purpose.
Maintenance
Related MCP Connectors
A time server that keeps your AI honest about time. Real clock + drift guard, zero dependencies.
Clockchain®: neutral verified network time for AI agents (get_time). Testnet.
A real clock for AI agents: current time, timezone conversion, and DST facts from the IANA tzdb.
Instruments for what a model cannot know on its own: time, randomness, DNS, hashes, receipts.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceProvides accurate time information from Network Time Protocol (NTP) servers with timezone support and security filtering. Features whitelist-based server approval, blocks unauthorized sources, and delivers structured time output with fallback mechanisms.MIT
- AlicenseAqualityCmaintenanceProvides high-accuracy time information by querying multiple NTP servers for consensus time, and comprehensive timezone support using IANA tzdata for conversions, DST handling, and clock drift detection independent of system time.73Apache 2.0
- AlicenseAqualityDmaintenanceProvides accurate UTC time by aggregating and verifying data from multiple reliable sources like WorldClockAPI, Google, and GitHub. It enables users to retrieve time in various formats including ISO, Unix timestamps, and human-readable strings with automatic source fallback for reliability.11MIT
- FlicenseNot gradedqualityCmaintenanceProvides real-time time awareness to Hermes Agent by exposing date, time, and timezone tools through MCP, enabling on-demand temporal queries.1-