modelcontextprotocol/servers · error · McpError

INVALID_PARAMS

INVALID_PARAMS

Error message

Invalid timezone: {str(e)}

What it means

get_zoneinfo (server.py:53-57) wraps ZoneInfo(timezone_name); any failure is re-raised as McpError(INVALID_PARAMS, 'Invalid timezone: ...'). ZoneInfo fails on non-IANA names ('PST', 'EST', 'UTC+5'), typos, or when the host lacks tz data (notably Windows without the 'tzdata' package). IMPORTANT: when reached via get_current_time/convert_time this McpError is caught by the outer except Exception (error 49) and downgraded to a plain ValueError, so the structured INVALID_PARAMS code never reaches the client.

Source

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


def get_local_tz(local_tz_override: str | None = None) -> ZoneInfo:
    if local_tz_override:
        return ZoneInfo(local_tz_override)

    # Get local timezone from datetime.now()
    local_tzname = get_localzone_name()
    if local_tzname is not None:
        return ZoneInfo(local_tzname)
    # Default to UTC if local timezone cannot be determined
    return ZoneInfo("UTC")


def get_zoneinfo(timezone_name: str) -> ZoneInfo:
    try:
        return ZoneInfo(timezone_name)
    except Exception as e:
        raise McpError(ErrorData(code=INVALID_PARAMS, message=f"Invalid timezone: {str(e)}"))


class TimeServer:
    def get_current_time(self, timezone_name: str) -> TimeResult:
        """Get current time in specified timezone"""
        timezone = get_zoneinfo(timezone_name)
        current_time = datetime.now(timezone)

        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:

View on GitHub (pinned to 76d64c822f)

Solutions

  1. Use a full IANA name: 'America/Los_Angeles', 'Europe/Berlin', 'UTC'.
  2. On Windows or slim images, install tzdata (uv add tzdata / pip install tzdata).
  3. Validate/normalize the timezone client-side against zoneinfo.available_timezones() before sending.
  4. If you maintain the server, narrow the outer except (error 49) so McpError is re-raised unchanged and the code survives.

Example fix

// before
//   timezone: "PST"            -> Invalid timezone
// after
//   timezone: "America/Los_Angeles"
Defensive patterns

Strategy: try-catch

Validate before calling

from zoneinfo import ZoneInfo, available_timezones

def valid_tz(name: str) -> str:
    if not isinstance(name, str) or name not in available_timezones():
        raise ValueError(f"not an IANA timezone: {name!r}")
    ZoneInfo(name)  # smoke test; catches missing tzdata on Windows
    return name

Type guard

from zoneinfo import available_timezones

def is_iana_tz(v: object) -> bool:
    return isinstance(v, str) and v in available_timezones()

Try / catch

from mcp.shared.exceptions import McpError

try:
    result = time_server.get_current_time(timezone)
except McpError as e:
    # INVALID_PARAMS; surface to user, suggest an IANA name
    handle_bad_timezone(e)

Prevention

When it happens

Trigger: Passing a POSIX abbreviation ('PST','EST'), an offset string ('UTC+2'), a typo ('America/LosAngelos'), or running on Windows/a slim container without tzdata installed.

Common situations: Users used to tz abbreviations. Windows dev hosts. Minimal Docker images missing the tzdata wheel. LLM-supplied timezone strings.

Related errors


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