Fix uv lock for time-server, update Readme

This commit is contained in:
Mariusz Korzekwa 2024-11-28 21:46:36 +01:00
commit d37ce3cc51
5 changed files with 21 additions and 124 deletions

View file

@ -5,8 +5,8 @@ A Model Context Protocol server that provides time and timezone conversion capab
### Available Tools ### Available Tools
- `get_current_time` - Get current time in a specific timezone or system timezone. - `get_current_time` - Get current time in a specific timezone or system timezone.
- Optional argument: `timezone` (string): IANA timezone name (e.g., 'America/New_York', 'Europe/London') - Required arguments:
- If timezone is not provided, returns time in system timezone - `timezone` (string): IANA timezone name (e.g., 'America/New_York', 'Europe/London')
- `convert_time` - Convert time between timezones. - `convert_time` - Convert time between timezones.
- Required arguments: - Required arguments:
@ -111,45 +111,25 @@ Example:
## Example Interactions ## Example Interactions
1. Get current time (using system timezone): 1. Get current time:
```json
{
"name": "get_current_time",
"arguments": {}
}
```
Response:
```json
{
"timezone": "Europe/London",
"time": "14:30 BST",
"date": "2024-11-25",
"full_datetime": "2024-11-25 14:30:00 BST",
"is_dst": true
}
```
2. Get current time in specific timezone:
```json ```json
{ {
"name": "get_current_time", "name": "get_current_time",
"arguments": { "arguments": {
"timezone": "America/New_York" "timezone": "Europe/Warsaw"
} }
} }
``` ```
Response: Response:
```json ```json
{ {
"timezone": "America/New_York", "timezone": "Europe/Warsaw",
"time": "09:30 EDT", "datetime": "2024-01-01T13:00:00+01:00",
"date": "2024-11-25", "is_dst": false
"full_datetime": "2024-11-25 09:30:00 EDT",
"is_dst": true
} }
``` ```
3. Convert time between timezones: 2. Convert time between timezones:
```json ```json
{ {
"name": "convert_time", "name": "convert_time",
@ -165,31 +145,18 @@ Response:
{ {
"source": { "source": {
"timezone": "America/New_York", "timezone": "America/New_York",
"time": "16:30 EDT", "datetime": "2024-01-01T12:30:00-05:00",
"date": "2024-11-25" "is_dst": false
}, },
"target": { "target": {
"timezone": "Asia/Tokyo", "timezone": "Asia/Tokyo",
"time": "05:30 JST", "datetime": "2024-01-01T12:30:00+09:00",
"date": "2024-11-26" "is_dst": false
}, },
"time_difference": "+13.0h", "time_difference": "+13.0h",
"date_changed": true,
"day_relation": "next day"
} }
``` ```
## Tips for Using IANA Timezone Names
Common timezone formats:
- North America: `America/New_York`, `America/Los_Angeles`, `America/Chicago`
- Europe: `Europe/London`, `Europe/Paris`, `Europe/Berlin`
- Asia: `Asia/Tokyo`, `Asia/Shanghai`, `Asia/Dubai`
- Pacific: `Pacific/Auckland`, `Pacific/Honolulu`
- Australia: `Australia/Sydney`, `Australia/Melbourne`
The server will automatically detect and use your system timezone if no specific timezone is provided.
## Debugging ## Debugging
You can use the MCP inspector to debug the server. For uvx installations: You can use the MCP inspector to debug the server. For uvx installations:

View file

@ -1,6 +1,6 @@
[project] [project]
name = "mcp-server-time" name = "mcp-server-time"
version = "0.5.1.pre3" version = "0.5.1"
description = "A Model Context Protocol server providing tools for time queries and timezone conversions for LLMs" description = "A Model Context Protocol server providing tools for time queries and timezone conversions for LLMs"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"
@ -33,5 +33,4 @@ dev-dependencies = [
"freezegun>=1.5.1", "freezegun>=1.5.1",
"pyright>=1.1.389", "pyright>=1.1.389",
"pytest>=8.3.3", "pytest>=8.3.3",
"ruff>=0.7.3",
] ]

View file

@ -1,5 +1,4 @@
from dataclasses import dataclass from datetime import datetime
from datetime import datetime, timedelta
from enum import Enum from enum import Enum
import json import json
from typing import Sequence from typing import Sequence
@ -35,13 +34,10 @@ class TimeConversionInput(BaseModel):
time: str time: str
target_tz_list: list[str] target_tz_list: list[str]
def get_local_tz(local_tz_override: str | None = None) -> pytz.timezone:
return pytz.timezone(local_tz_override) if local_tz_override else get_localzone()
class TimeServer: class TimeServer:
def __init__(self, local_tz_override: str | None = None):
self.local_tz = (
pytz.timezone(local_tz_override) if local_tz_override else get_localzone()
)
def get_current_time(self, timezone_name: str) -> TimeResult: def get_current_time(self, timezone_name: str) -> TimeResult:
"""Get current time in specified timezone""" """Get current time in specified timezone"""
try: try:
@ -109,8 +105,8 @@ class TimeServer:
async def serve(local_timezone: str | None = None) -> None: async def serve(local_timezone: str | None = None) -> None:
server = Server("mcp-time") server = Server("mcp-time")
time_server = TimeServer(local_timezone) time_server = TimeServer()
local_tz = str(time_server.local_tz) local_tz = str(get_local_tz(local_timezone))
@server.list_tools() @server.list_tools()
async def list_tools() -> list[Tool]: async def list_tools() -> list[Tool]:
@ -183,7 +179,7 @@ async def serve(local_timezone: str | None = None) -> None:
case _: case _:
raise ValueError(f"Unknown tool: {name}") raise ValueError(f"Unknown tool: {name}")
return [TextContent(type="text", text=json.dumps(result, indent=2))] return [TextContent(type="text", text=json.dumps(result.model_dump(), indent=2))]
except Exception as e: except Exception as e:
raise ValueError(f"Error processing mcp-server-time query: {str(e)}") raise ValueError(f"Error processing mcp-server-time query: {str(e)}")

View file

@ -3,7 +3,7 @@ import os
from freezegun import freeze_time from freezegun import freeze_time
import pytest import pytest
from mcp_server_time.server import TimeServer from mcp_server_time.server import TimeServer, serve
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -455,41 +455,3 @@ def test_convert_time(test_time, source_tz, time_str, target_tz, expected):
assert result.source.is_dst == expected["source"]["is_dst"] assert result.source.is_dst == expected["source"]["is_dst"]
assert result.target.is_dst == expected["target"]["is_dst"] assert result.target.is_dst == expected["target"]["is_dst"]
assert result.time_difference == expected["time_difference"] assert result.time_difference == expected["time_difference"]
# @pytest.mark.anyio
# async def test_call_tool(mock_forecast_response):
# class Response():
# def raise_for_status(self):
# pass
# def json(self):
# return mock_forecast_response
# class AsyncClient():
# def __aenter__(self):
# return self
# async def __aexit__(self, *exc_info):
# pass
# async def get(self, *args, **kwargs):
# return Response()
# with patch('httpx.AsyncClient', new=AsyncClient) as mock_client:
# result = await call_tool("get_forecast", {"city": "London", "days": 2})
# assert len(result) == 1
# assert result[0].type == "text"
# forecast_data = json.loads(result[0].text)
# assert len(forecast_data) == 1
# assert forecast_data[0]["temperature"] == 18.5
# assert forecast_data[0]["conditions"] == "sunny"
# @pytest.mark.anyio
# async def test_list_tools():
# tools = await list_tools()
# assert len(tools) == 1
# assert tools[0].name == "get_forecast"
# assert "city" in tools[0].inputSchema["properties"]

29
src/time/uv.lock generated
View file

@ -160,7 +160,7 @@ wheels = [
[[package]] [[package]]
name = "mcp-server-time" name = "mcp-server-time"
version = "0.5.1rc3" version = "0.5.1"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "mcp" }, { name = "mcp" },
@ -174,7 +174,6 @@ dev = [
{ name = "freezegun" }, { name = "freezegun" },
{ name = "pyright" }, { name = "pyright" },
{ name = "pytest" }, { name = "pytest" },
{ name = "ruff" },
] ]
[package.metadata] [package.metadata]
@ -190,7 +189,6 @@ dev = [
{ name = "freezegun", specifier = ">=1.5.1" }, { name = "freezegun", specifier = ">=1.5.1" },
{ name = "pyright", specifier = ">=1.1.389" }, { name = "pyright", specifier = ">=1.1.389" },
{ name = "pytest", specifier = ">=8.3.3" }, { name = "pytest", specifier = ">=8.3.3" },
{ name = "ruff", specifier = ">=0.7.3" },
] ]
[[package]] [[package]]
@ -360,31 +358,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 },
] ]
[[package]]
name = "ruff"
version = "0.8.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b2/d6/a2373f3ba7180ddb44420d2a9d1f1510e1a4d162b3d27282bedcb09c8da9/ruff-0.8.0.tar.gz", hash = "sha256:a7ccfe6331bf8c8dad715753e157457faf7351c2b69f62f32c165c2dbcbacd44", size = 3276537 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/77/e889ee3ce7fd8baa3ed1b77a03b9fb8ec1be68be1418261522fd6a5405e0/ruff-0.8.0-py3-none-linux_armv6l.whl", hash = "sha256:fcb1bf2cc6706adae9d79c8d86478677e3bbd4ced796ccad106fd4776d395fea", size = 10518283 },
{ url = "https://files.pythonhosted.org/packages/da/c8/0a47de01edf19fb22f5f9b7964f46a68d0bdff20144d134556ffd1ba9154/ruff-0.8.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:295bb4c02d58ff2ef4378a1870c20af30723013f441c9d1637a008baaf928c8b", size = 10317691 },
{ url = "https://files.pythonhosted.org/packages/41/17/9885e4a0eeae07abd2a4ebabc3246f556719f24efa477ba2739146c4635a/ruff-0.8.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7b1f1c76b47c18fa92ee78b60d2d20d7e866c55ee603e7d19c1e991fad933a9a", size = 9940999 },
{ url = "https://files.pythonhosted.org/packages/3e/cd/46b6f7043597eb318b5f5482c8ae8f5491cccce771e85f59d23106f2d179/ruff-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb0d4f250a7711b67ad513fde67e8870109e5ce590a801c3722580fe98c33a99", size = 10772437 },
{ url = "https://files.pythonhosted.org/packages/5d/87/afc95aeb8bc78b1d8a3461717a4419c05aa8aa943d4c9cbd441630f85584/ruff-0.8.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e55cce9aa93c5d0d4e3937e47b169035c7e91c8655b0974e61bb79cf398d49c", size = 10299156 },
{ url = "https://files.pythonhosted.org/packages/65/fa/04c647bb809c4d65e8eae1ed1c654d9481b21dd942e743cd33511687b9f9/ruff-0.8.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f4cd64916d8e732ce6b87f3f5296a8942d285bbbc161acee7fe561134af64f9", size = 11325819 },
{ url = "https://files.pythonhosted.org/packages/90/26/7dad6e7d833d391a8a1afe4ee70ca6f36c4a297d3cca83ef10e83e9aacf3/ruff-0.8.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c5c1466be2a2ebdf7c5450dd5d980cc87c8ba6976fb82582fea18823da6fa362", size = 12023927 },
{ url = "https://files.pythonhosted.org/packages/24/a0/be5296dda6428ba8a13bda8d09fbc0e14c810b485478733886e61597ae2b/ruff-0.8.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2dabfd05b96b7b8f2da00d53c514eea842bff83e41e1cceb08ae1966254a51df", size = 11589702 },
{ url = "https://files.pythonhosted.org/packages/26/3f/7602eb11d2886db545834182a9dbe500b8211fcbc9b4064bf9d358bbbbb4/ruff-0.8.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:facebdfe5a5af6b1588a1d26d170635ead6892d0e314477e80256ef4a8470cf3", size = 12782936 },
{ url = "https://files.pythonhosted.org/packages/4c/5d/083181bdec4ec92a431c1291d3fff65eef3ded630a4b55eb735000ef5f3b/ruff-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87a8e86bae0dbd749c815211ca11e3a7bd559b9710746c559ed63106d382bd9c", size = 11138488 },
{ url = "https://files.pythonhosted.org/packages/b7/23/c12cdef58413cee2436d6a177aa06f7a366ebbca916cf10820706f632459/ruff-0.8.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:85e654f0ded7befe2d61eeaf3d3b1e4ef3894469cd664ffa85006c7720f1e4a2", size = 10744474 },
{ url = "https://files.pythonhosted.org/packages/29/61/a12f3b81520083cd7c5caa24ba61bb99fd1060256482eff0ef04cc5ccd1b/ruff-0.8.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:83a55679c4cb449fa527b8497cadf54f076603cc36779b2170b24f704171ce70", size = 10369029 },
{ url = "https://files.pythonhosted.org/packages/08/2a/c013f4f3e4a54596c369cee74c24870ed1d534f31a35504908b1fc97017a/ruff-0.8.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:812e2052121634cf13cd6fddf0c1871d0ead1aad40a1a258753c04c18bb71bbd", size = 10867481 },
{ url = "https://files.pythonhosted.org/packages/d5/f7/685b1e1d42a3e94ceb25eab23c70bdd8c0ab66a43121ef83fe6db5a58756/ruff-0.8.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:780d5d8523c04202184405e60c98d7595bdb498c3c6abba3b6d4cdf2ca2af426", size = 11237117 },
{ url = "https://files.pythonhosted.org/packages/03/20/401132c0908e8837625e3b7e32df9962e7cd681a4df1e16a10e2a5b4ecda/ruff-0.8.0-py3-none-win32.whl", hash = "sha256:5fdb6efecc3eb60bba5819679466471fd7d13c53487df7248d6e27146e985468", size = 8783511 },
{ url = "https://files.pythonhosted.org/packages/1d/5c/4d800fca7854f62ad77f2c0d99b4b585f03e2d87a6ec1ecea85543a14a3c/ruff-0.8.0-py3-none-win_amd64.whl", hash = "sha256:582891c57b96228d146725975fbb942e1f30a0c4ba19722e692ca3eb25cc9b4f", size = 9559876 },
{ url = "https://files.pythonhosted.org/packages/5b/bc/cc8a6a5ca4960b226dc15dd8fb511dd11f2014ff89d325c0b9b9faa9871f/ruff-0.8.0-py3-none-win_arm64.whl", hash = "sha256:ba93e6294e9a737cd726b74b09a6972e36bb511f9a102f1d9a7e1ce94dd206a6", size = 8939733 },
]
[[package]] [[package]]
name = "six" name = "six"
version = "1.16.0" version = "1.16.0"