modelcontextprotocol/servers · error · ValueError

Invalid time format. Expected HH:MM [24-hour format]

Error message

Invalid time format. Expected HH:MM [24-hour format]

What it means

convert_time parses time_str with datetime.strptime(time_str, '%H:%M') (server.py:80-83); any strptime ValueError is re-raised as ValueError('Invalid time format. Expected HH:MM [24-hour format]'). Only 24-hour HH:MM is accepted (12-hour, AM/PM, seconds, out-of-range, and empty strings are rejected). Like errors 44/46/47/48, the outer handler (error 49) re-wraps this before it leaves the server.

Source

Thrown at src/time/src/mcp_server_time/server.py:83

        return TimeResult(
            timezone=timezone_name,
            datetime=current_time.isoformat(timespec="seconds"),
            day_of_week=current_time.strftime("%A"),
            is_dst=bool(current_time.dst()),
        )

    def convert_time(
        self, source_tz: str, time_str: str, target_tz: str
    ) -> TimeConversionResult:
        """Convert time between timezones"""
        source_timezone = get_zoneinfo(source_tz)
        target_timezone = get_zoneinfo(target_tz)

        try:
            parsed_time = datetime.strptime(time_str, "%H:%M").time()
        except ValueError:
            raise ValueError("Invalid time format. Expected HH:MM [24-hour format]")

        now = datetime.now(source_timezone)
        source_time = datetime(
            now.year,
            now.month,
            now.day,
            parsed_time.hour,
            parsed_time.minute,
            tzinfo=source_timezone,
        )

        target_time = source_time.astimezone(target_timezone)
        source_offset = source_time.utcoffset() or timedelta()
        target_offset = target_time.utcoffset() or timedelta()
        hours_difference = (target_offset - source_offset).total_seconds() / 3600

        if hours_difference.is_integer():
            time_diff_str = f"{hours_difference:+.1f}h"

View on GitHub (pinned to 76d64c822f)

Solutions

  1. Send exactly zero-padded 24-hour HH:MM (e.g. '14:30', '09:05').
  2. Normalize client-side: parse the user's input, then re-emit strftime('%H:%M').
  3. Reject empty or None before constructing the call.

Example fix

// before
//   time: "2:30 PM"   -> Invalid time format
// after
//   time: "14:30"
Defensive patterns

Strategy: validation

Validate before calling

from datetime import datetime

def normalize_hhmm(s: str) -> str:
    # raises ValueError if not parseable as 24-hour HH:MM
    return datetime.strptime(s, "%H:%M").strftime("%H:%M")

time_str = normalize_hhmm(arguments["time"])

Type guard

import re

def is_hhmm(v: object) -> bool:
    return isinstance(v, str) and bool(re.fullmatch(r"([01]\d|2[0-3]):[0-5]\d", v))

Prevention

When it happens

Trigger: Passing '2:30 PM', '14:30:00', '09:00 AM', '25:00', 'noon', or '' as the 'time' argument.

Common situations: 12-hour habits, natural-language times from an LLM ('2pm'), locale-formatted strings, or a stray seconds component.

Related errors


AI-assisted analysis of modelcontextprotocol/servers@76d64c822f (2026-08-12). Data as JSON: /api/errors/a9922f095d4ae17f. Report an issue: GitHub.