diff --git a/src/time/pyproject.toml b/src/time/pyproject.toml index f2afd6a3..d1ec316a 100644 --- a/src/time/pyproject.toml +++ b/src/time/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "mcp-server-time" -version = "0.1.0" +version = "0.5.1" description = "A Model Context Protocol server providing tools for time queries and timezone conversions for LLMs" readme = "README.md" requires-python = ">=3.10" -authors = [{ name = "Your Name" }] +authors = [{ name = "Mariusz 'maledorak' Korzekwa", email = "mariusz@korzekwa.dev" }] keywords = ["time", "timezone", "mcp", "llm"] license = { text = "MIT" } classifiers = [ diff --git a/src/time/src/mcp_server_time/server-resources.py b/src/time/src/mcp_server_time/server-resources.py deleted file mode 100644 index 23ae45e7..00000000 --- a/src/time/src/mcp_server_time/server-resources.py +++ /dev/null @@ -1,167 +0,0 @@ -# server.py - -from datetime import datetime -from typing import Dict, Any, Tuple, Optional -import pytz -from tzlocal import get_localzone -from mcp.server import Server -from mcp.server.stdio import stdio_server -from mcp.types import Resource, ResourceTemplate, ListResourcesResult, ListResourceTemplatesResult -from pydantic import AnyUrl -import json - - -class TimeServer: - def __init__(self, local_tz_override: Optional[str] = None): - self.local_tz = pytz.timezone(local_tz_override) if local_tz_override else get_localzone() - - def get_current_time(self, timezone_name: str | None = None) -> Dict[str, Any]: - """Get current time in specified timezone or local timezone if none specified""" - timezone = pytz.timezone(timezone_name) if timezone_name else self.local_tz - current_time = datetime.now(timezone) - - return { - "timezone": timezone_name or str(self.local_tz), - "time": current_time.strftime("%H:%M %Z"), - "date": current_time.strftime("%Y-%m-%d"), - "full_datetime": current_time.strftime("%Y-%m-%d %H:%M:%S %Z"), - "is_dst": bool(current_time.dst()) - } - - def parse_time_str(self, time_str: str) -> Tuple[int, int]: - """Parse time string in format HH:MM""" - try: - hour, minute = map(int, time_str.split(":")) - if not (0 <= hour <= 23 and 0 <= minute <= 59): - raise ValueError - return hour, minute - except: - raise ValueError("Invalid time format. Expected HH:MM (24-hour format)") - - def get_timezone(self, tz_name: str) -> pytz.timezone: - """Get timezone object, handling 'local' keyword""" - if tz_name.lower() == 'local': - return self.local_tz - try: - return pytz.timezone(tz_name) - except pytz.exceptions.UnknownTimeZoneError: - raise ValueError(f"Unknown timezone: {tz_name}") - - def convert_time(self, source_tz: str, time_str: str, target_tz: str) -> Dict[str, Any]: - """Convert time between timezones""" - source_timezone = self.get_timezone(source_tz) - target_timezone = self.get_timezone(target_tz) - - hour, minute = self.parse_time_str(time_str) - - now = datetime.now(source_timezone) - source_time = source_timezone.localize( - datetime(now.year, now.month, now.day, hour, minute) - ) - - target_time = source_time.astimezone(target_timezone) - date_changed = source_time.date() != target_time.date() - - return { - "source": { - "timezone": str(source_timezone), - "time": source_time.strftime("%H:%M %Z"), - "date": source_time.strftime("%Y-%m-%d"), - "full_datetime": source_time.strftime("%Y-%m-%d %H:%M:%S %Z") - }, - "target": { - "timezone": str(target_timezone), - "time": target_time.strftime("%H:%M %Z"), - "date": target_time.strftime("%Y-%m-%d"), - "full_datetime": target_time.strftime("%Y-%m-%d %H:%M:%S %Z") - }, - "time_difference": f"{(target_time.utcoffset() - source_time.utcoffset()).total_seconds() / 3600:+.1f}h", - "date_changed": date_changed, - "day_relation": "next day" if date_changed and target_time.date() > source_time.date() else "previous day" if date_changed else "same day" - } - - async def handle_uri(self, uri: str) -> str: - """Main URI handler that routes to appropriate method""" - try: - if uri == "time://query": - result = self.get_current_time() - elif uri.startswith("time://query/"): - timezone_name = uri.replace("time://query/", "") - result = self.get_current_time(timezone_name) - elif uri.startswith("time://convert/"): - path = uri.replace("time://convert/", "") - source_parts = path.split("/to/") - if len(source_parts) != 2: - raise ValueError("Invalid conversion URI format") - - source_info, target_tz = source_parts - source_parts = source_info.split("/") - if len(source_parts) != 2: - raise ValueError("Invalid source time format") - - source_tz, time_str = source_parts - result = self.convert_time(source_tz, time_str, target_tz) - else: - raise ValueError(f"Unsupported URI format: {uri}") - - return json.dumps(result) - - except Exception as e: - raise ValueError(f"Error processing time query: {str(e)}") - - -async def serve(local_timezone: Optional[str] = None) -> None: - server = Server("mcp-time") - time_server = TimeServer(local_timezone) - - @server.list_resources() - async def list_resources() -> list[Resource]: - current_tz = str(time_server.local_tz) - - return [ - Resource( - uri=AnyUrl("time://query"), - name="Local Time Query", - description=f"Get current time in your local timezone ({current_tz})", - mimeType="application/json" - ) - ] - # resourceTemplates=[ - # ResourceTemplate( - # uriTemplate="time://query/{timezone}", - # name="Timezone Time Query", - # description="Get current time in a specific timezone", - # mimeType="application/json" - # ), - # ResourceTemplate( - # uriTemplate="time://convert/{source_timezone}/{hour}:{minute}/to/{target_timezone}", - # name="Timezone Conversion", - # description="Convert specific time between timezones (use 'local' for local timezone)", - # mimeType="application/json" - # ) - # ] - - # @server.list_resources() - # async def list_resource_templates() -> list[ResourceTemplate]: - # return [ - # ResourceTemplate( - # uriTemplate="time://query/{timezone}", - # name="Timezone Time Query", - # description="Get current time in a specific timezone", - # mimeType="application/json" - # ), - # ResourceTemplate( - # uriTemplate="time://convert/{source_timezone}/{hour}:{minute}/to/{target_timezone}", - # name="Timezone Conversion", - # description="Convert specific time between timezones (use 'local' for local timezone)", - # mimeType="application/json" - # ) - # ] - - @server.read_resource() - async def read_resource(uri: AnyUrl) -> str: - return await time_server.handle_uri(str(uri)) - - options = server.create_initialization_options() - async with stdio_server() as (read_stream, write_stream): - await server.run(read_stream, write_stream, options) diff --git a/src/time/uv.lock b/src/time/uv.lock index ca42233f..d74ecc2d 100644 --- a/src/time/uv.lock +++ b/src/time/uv.lock @@ -139,7 +139,7 @@ wheels = [ [[package]] name = "mcp-server-time" -version = "0.1.0" +version = "0.5.1" source = { editable = "." } dependencies = [ { name = "mcp" },