timezone-mcp
by osteele
README.md
# timezone-mcp
`timezone-mcp` is a local MCP server that converts timestamps between time zones. It exposes one tool, `convert_time`.
The server uses IANA time zone rules, including daylight saving time at the specified instant, and returns the source, UTC, and converted times with their dates.
## Agents often get date boundaries wrong
As of mid-2026, general-purpose agents often answer time zone questions confidently but incorrectly. Errors are especially common around daylight saving transitions and conversions that cross a calendar date. `timezone-mcp` delegates the calculation to Python's IANA time zone database and returns each date explicitly.
## Install
Requires [`uv`](https://docs.astral.sh/uv/), which installs Python for the tool if the machine does not have a suitable version. Nothing to clone: [add-mcp](https://github.com/neon-solutions/add-mcp) registers the server with your agent in one command, and knows where each client keeps its config.
```bash
npx add-mcp "$(command -v uvx)" \
--args --from --args git+https://github.com/osteele/timezone-mcp@v0.1.0 --args timezone-mcp \
--name timezone-mcp \
--env TIMEZONE_MCP_ALWAYS_TIMEZONES="China,Eastern" \
--global --agent claude-code --agent codex
```
Every argument after the command needs its own `--args`. add-mcp does not split a quoted command string, so folding them into the first argument writes a command name containing spaces, without reporting an error. Drop `--global` to register the server for one project instead of the whole machine, and see `npx add-mcp list-agents` for the other clients it supports.
That writes an entry equivalent to this, which you can also add by hand to whichever file your client uses:
```json
{
"timezone-mcp": {
"command": "/absolute/path/to/uvx",
"args": [
"--from",
"git+https://github.com/osteele/timezone-mcp@v0.1.0",
"timezone-mcp"
],
"env": {
"TIMEZONE_MCP_ALWAYS_TIMEZONES": "China,Eastern"
}
}
}
```
Claude Desktop keeps that file at `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, under `mcpServers`; open it from the developer settings, and restart the app after editing. Claude Code and Codex also take it from their own CLIs:
```bash
claude mcp add --scope user timezone-mcp \
-e TIMEZONE_MCP_ALWAYS_TIMEZONES="China,Eastern" \
-- "$(command -v uvx)" --from git+https://github.com/osteele/timezone-mcp@v0.1.0 timezone-mcp
codex mcp add timezone-mcp \
--env TIMEZONE_MCP_ALWAYS_TIMEZONES="China,Eastern" \
-- "$(command -v uvx)" --from git+https://github.com/osteele/timezone-mcp@v0.1.0 timezone-mcp
```
`@v0.1.0` pins the install to a tagged release, so a later push to `main` cannot change what an already-registered client runs. Drop it to track `main`, or raise it when a newer tag exists.
To run the server by hand, `uvx --from git+https://github.com/osteele/timezone-mcp@v0.1.0 timezone-mcp` waits for MCP messages on standard input, so it will appear to hang. That is what a client expects; press Ctrl-C.
## Configuration
Every result carries the zones you asked for. `TIMEZONE_MCP_ALWAYS_TIMEZONES` adds zones that appear in every result without being requested. With no configuration, those standing zones are China and U.S. Eastern.
Set it to a comma-separated list or a JSON array:
```bash
export TIMEZONE_MCP_ALWAYS_TIMEZONES="China,Eastern"
```
The JSON value `[]` disables standing zones entirely. An empty string is rejected.
For file-based configuration, save a JSON file such as `/absolute/path/to/timezone-mcp.json`:
```json
{
"always_timezones": ["China", "Eastern"]
}
```
Then set its path:
```bash
export TIMEZONE_MCP_CONFIG=/absolute/path/to/timezone-mcp.json
```
`TIMEZONE_MCP_ALWAYS_TIMEZONES` takes precedence when both variables are set.
Standing and requested zones are combined, and repeated names are deduplicated after normalization. A zone is dropped from the result when its local clock time and UTC offset match the source at that instant. A Los Angeles result is dropped for a Phoenix source when both are on UTC−07:00, for example, even though their daylight saving rules differ.
## Tool arguments
- `time`: An ISO-like timestamp with a clock time, such as `2026-07-16 09:30`, `2026-07-16T09:30:00`, or `2026-07-16T09:30:00-04:00`. Date-only values are rejected.
- `source_timezone`: The source time zone for a timestamp without a UTC offset. Omit it when `time` already contains an offset.
- `output_timezones`: An optional list of additional output time zones.
- `fold`: `0` selects the first occurrence of an ambiguous local time, and `1` selects the second occurrence during a fall daylight saving transition. The default is `0`.
Local times that do not exist during a spring daylight saving transition are rejected.
### Time zone names
Any zone argument accepts an IANA name, such as `America/Los_Angeles` or `Europe/Berlin`.
Five zones also have short aliases, matched case-insensitively:
| Zone | Aliases |
| --- | --- |
| `AoE` (fixed UTC−12) | `aoe`, `anywhere on earth` |
| `Asia/Shanghai` | `china`, `beijing`, `shanghai`, `china standard time`, `cst china`, `beijing time` |
| `America/New_York` | `eastern`, `et`, `est`, `edt`, `us eastern`, `u.s. eastern`, `new york`, `eastern time` |
| `America/Los_Angeles` | `pacific`, `pt`, `pst`, `pdt`, `us pacific`, `u.s. pacific`, `pacific time` |
| `UTC` | `utc`, `z` |
That table is the whole alias set. Anything else must be a valid IANA name; an unrecognized name is rejected with `unknown timezone`.
`AoE` is Anywhere on Earth, the fixed UTC−12 offset that conference and journal deadlines are usually quoted in. It is the last place on the planet where a given calendar date is still in progress, so a deadline stated as AoE expires later than the same wall-clock time anywhere else.
## Result
The result contains the resolved input, UTC time, standing zones, conversions, and date-boundary information. Selected fields from a China-to-Eastern conversion look like this:
```json
{
"utc": {
"datetime": "2026-07-16T01:00:00+00:00"
},
"configured_timezones": [
"Asia/Shanghai",
"America/New_York"
],
"conversions": [
{
"datetime": "2026-07-15T21:00:00-04:00",
"timezone": "America/New_York",
"abbreviation": "EDT",
"requested_timezone": "America/New_York"
}
],
"date_boundary": {
"source_date": "2026-07-16",
"different_dates": ["America/New_York"]
}
}
```
`date_boundary.different_dates` names the zones whose calendar date differs from the source, which is the case these conversions most often get wrong.
Each formatted timestamp also includes `date`, `time`, `weekday`, `abbreviation`, and `utc_offset` fields.
## Development
```bash
git clone https://github.com/osteele/timezone-mcp
cd timezone-mcp
uv sync
uv run --frozen pytest
uv run --frozen ruff check .
uv run --frozen ty check
```
To point a client at a checkout rather than at the published source, replace the `--from git+...` argument with `--directory /absolute/path/to/timezone-mcp run --no-dev`, invoking `uv` instead of `uvx`.
## Related
`timezone-mcp` is one of a set of tools for agent sessions and the environments
they run in, listed at
[osteele.com/software/agent-tools](https://osteele.com/software/agent-tools).
TDQS
A4.2/5.0
Scored across 1 tool
Disambiguation5/5
With only one tool, there is no possibility of confusion or overlap. The tool's purpose is clearly defined and unambiguous.
Naming Consistency5/5
The single tool name `convert_time` follows a clean verb_noun pattern, which is internally consistent.
Tool Count3/5
The server offers just one tool, which feels thin for a general timezone utility. While the core conversion feature is present, users might expect additional operations like timezone listing or validation.
Completeness4/5
The conversion operation is fully covered, but the surface lacks an explicit way to list available timezones or configured defaults, which would be a minor gap for a timezone-focused MCP.
Maintenance
ActivitySlowing
ResponsivenessNo issues