Skip to content

Commit 1559cbe

Browse files
ivanvia7jirispilka
andauthored
Feat: Time MCP Server (#51)
Co-authored-by: Jiří Spilka <[email protected]>
1 parent 39a750e commit 1559cbe

File tree

14 files changed

+1146
-0
lines changed

14 files changed

+1146
-0
lines changed

time-mcp-server/.actor/actor.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"actorSpecification": 1,
3+
"name": "time-mcp-server",
4+
"title": "Time MCP Server",
5+
"description": "A Python server implementing the Model Context Protocol (MCP) for time-related operations.",
6+
"version": "0.1",
7+
"buildTag": "latest",
8+
"usesStandbyMode": true,
9+
"meta": {
10+
"templateId": "python-mcp-server"
11+
},
12+
"dockerfile": "../Dockerfile",
13+
"webServerMcpPath": "/mcp"
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"get_current_time": {
3+
"eventTitle": "Get Current Time",
4+
"eventDescription": "Fee for retrieving the current time in a specified timezone using the get_current_time tool.",
5+
"eventPriceUsd": 0.0005
6+
},
7+
"convert_time": {
8+
"eventTitle": "Convert Time Between Timezones",
9+
"eventDescription": "Fee for converting time between timezones using the convert_time tool.",
10+
"eventPriceUsd": 0.0005
11+
}
12+
}

time-mcp-server/.gitignore

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
.mise.toml
2+
.nvim.lua
3+
storage
4+
5+
# The rest is copied from https://github.com/github/gitignore/blob/main/Python.gitignore
6+
7+
# Byte-compiled / optimized / DLL files
8+
__pycache__/
9+
*.py[cod]
10+
*$py.class
11+
12+
# C extensions
13+
*.so
14+
15+
# Distribution / packaging
16+
.Python
17+
build/
18+
develop-eggs/
19+
dist/
20+
downloads/
21+
eggs/
22+
.eggs/
23+
lib/
24+
lib64/
25+
parts/
26+
sdist/
27+
var/
28+
wheels/
29+
share/python-wheels/
30+
*.egg-info/
31+
.installed.cfg
32+
*.egg
33+
MANIFEST
34+
35+
# PyInstaller
36+
# Usually these files are written by a python script from a template
37+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
38+
*.manifest
39+
*.spec
40+
41+
# Installer logs
42+
pip-log.txt
43+
pip-delete-this-directory.txt
44+
45+
# Unit test / coverage reports
46+
htmlcov/
47+
.tox/
48+
.nox/
49+
.coverage
50+
.coverage.*
51+
.cache
52+
nosetests.xml
53+
coverage.xml
54+
*.cover
55+
*.py,cover
56+
.hypothesis/
57+
.pytest_cache/
58+
cover/
59+
60+
# Translations
61+
*.mo
62+
*.pot
63+
64+
# Django stuff:
65+
*.log
66+
local_settings.py
67+
db.sqlite3
68+
db.sqlite3-journal
69+
70+
# Flask stuff:
71+
instance/
72+
.webassets-cache
73+
74+
# Scrapy stuff:
75+
.scrapy
76+
77+
# Sphinx documentation
78+
docs/_build/
79+
80+
# PyBuilder
81+
.pybuilder/
82+
target/
83+
84+
# Jupyter Notebook
85+
.ipynb_checkpoints
86+
87+
# IPython
88+
profile_default/
89+
ipython_config.py
90+
91+
# pyenv
92+
# For a library or package, you might want to ignore these files since the code is
93+
# intended to run in multiple environments; otherwise, check them in:
94+
.python-version
95+
96+
# pdm
97+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
98+
#pdm.lock
99+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
100+
# in version control.
101+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
102+
.pdm.toml
103+
.pdm-python
104+
.pdm-build/
105+
106+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
107+
__pypackages__/
108+
109+
# Celery stuff
110+
celerybeat-schedule
111+
celerybeat.pid
112+
113+
# SageMath parsed files
114+
*.sage.py
115+
116+
# Environments
117+
.env
118+
.venv
119+
env/
120+
venv/
121+
ENV/
122+
env.bak/
123+
venv.bak/
124+
125+
# Spyder project settings
126+
.spyderproject
127+
.spyproject
128+
129+
# Rope project settings
130+
.ropeproject
131+
132+
# mkdocs documentation
133+
/site
134+
135+
# mypy
136+
.mypy_cache/
137+
.dmypy.json
138+
dmypy.json
139+
140+
# Pyre type checker
141+
.pyre/
142+
143+
# pytype static type analyzer
144+
.pytype/
145+
146+
# Cython debug symbols
147+
cython_debug/
148+
149+
# PyCharm
150+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
151+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
152+
# and can be added to the global gitignore or merged into this file. For a more nuclear
153+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
154+
.idea/
155+
156+
# Visual Studio Code
157+
# Ignores the folder created by VS Code when changing workspace settings, doing debugger
158+
# configuration, etc. Can be commented out to share Workspace Settings within a team
159+
.vscode
160+
161+
# Zed editor
162+
# Ignores the folder created when setting Project Settings in the Zed editor. Can be commented out
163+
# to share Project Settings within a team
164+
.zed
165+
166+
# Added by Apify CLI
167+
node_modules

time-mcp-server/Dockerfile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# First, specify the base Docker image.
2+
# You can see the Docker images from Apify at https://hub.docker.com/r/apify/.
3+
# You can also use any other image from Docker Hub.
4+
FROM apify/actor-python:3.13
5+
6+
# Second, copy just requirements.txt into the Actor image,
7+
# since it should be the only file that affects the dependency installation in the next step,
8+
# in order to speed up the build.
9+
COPY requirements.txt ./
10+
11+
# Install the packages specified in requirements.txt,
12+
# print the installed Python version, pip version,
13+
# and all installed packages with their versions for debugging.
14+
RUN echo "Python version:" \
15+
&& python --version \
16+
&& echo "Pip version:" \
17+
&& pip --version \
18+
&& echo "Installing dependencies:" \
19+
&& pip install -r requirements.txt \
20+
&& echo "All installed Python packages:" \
21+
&& pip freeze
22+
23+
# Next, copy the remaining files and directories with the source code.
24+
# Since we do this after installing the dependencies, quick builds will be really fast
25+
# for most source file changes.
26+
COPY . ./
27+
28+
# Use compileall to ensure the runnability of the Actor Python code.
29+
RUN python3 -m compileall -q src/
30+
31+
# Specify how to launch the source code of your Actor.
32+
# By default, the "python3 -m ." command is run.
33+
CMD ["python3", "-m", "src"]

time-mcp-server/README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# ⏰ Time MCP Server (Apify Actor)
2+
3+
This Actor provides a Model Context Protocol (MCP) server for time and timezone conversion, running on the Apify platform. It enables LLMs and other clients to get current time information and perform timezone conversions using IANA timezone names, with automatic system timezone detection.
4+
5+
---
6+
7+
## 🚩 Claim this MCP server
8+
All credits to the original authors of <https://github.com/modelcontextprotocol/servers/tree/main/src/time>
9+
To claim this server, please write to [[email protected]](mailto:[email protected]).
10+
11+
---
12+
## Features
13+
14+
- **Current time lookup** in any IANA timezone
15+
- **Timezone conversion** between arbitrary IANA timezones
16+
- **Automatic system timezone detection** (can be overridden)
17+
- **Stdio MCP server** integration (runs `mcp-server-time` via `uvx`)
18+
- **Pay Per Event (PPE) billing** for each tool call
19+
- **Ready for Claude, Zed, VS Code, and other MCP clients**
20+
21+
---
22+
23+
## Available Tools
24+
25+
- `get_current_time` – Get current time in a specific timezone or system timezone
26+
- Arguments: `timezone` (string, IANA name, e.g. 'Europe/London')
27+
- `convert_time` – Convert time between timezones
28+
- Arguments: `source_timezone` (string), `time` (string, HH:MM), `target_timezone` (string)
29+
30+
---
31+
32+
## Usage
33+
34+
### Local run
35+
36+
```sh
37+
apify run -p
38+
```
39+
40+
### Example Interactions
41+
42+
**Get current time:**
43+
44+
```json
45+
{
46+
"name": "get_current_time",
47+
"arguments": { "timezone": "Europe/Warsaw" }
48+
}
49+
```
50+
51+
**Convert time:**
52+
53+
```json
54+
{
55+
"name": "convert_time",
56+
"arguments": {
57+
"source_timezone": "America/New_York",
58+
"time": "16:30",
59+
"target_timezone": "Asia/Tokyo"
60+
}
61+
}
62+
```
63+
64+
---
65+
66+
## Configuration
67+
68+
By default, the Actor launches `mcp-server-time` via `uvx` (recommended). To override the system timezone, set the `--local-timezone` argument in the command.
69+
70+
For pip installs, adjust the command to `python` and args to `['-m', 'mcp_server_time']` in both `src/main.py` and your client config.
71+
72+
---
73+
74+
## Billing (Pay Per Event)
75+
76+
Each tool call is billed per event. See `.actor/pay_per_event.json` and `src/const.py`:
77+
78+
```json
79+
{
80+
"get_current_time": { "price": 0.0005 },
81+
"convert_time": { "price": 0.0005 }
82+
}
83+
```
84+
85+
---
86+
87+
## Links & Resources
88+
89+
- [MCP servers repo](https://github.com/apify/mcp-servers)
90+
- [MCP docs](https://mcp.apify.com)
91+
- [Model Context Protocol](https://modelcontextprotocol.io)
92+
- [Apify SDK for Python](https://docs.apify.com/sdk/python)
93+
94+
---
95+
96+
## Claim this server
97+
98+
To claim ownership or manage this MCP server, visit the [Model Context Protocol claim page](https://github.com/modelcontextprotocol/servers/tree/main/src/time).

time-mcp-server/requirements.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Feel free to add your Python dependencies below. For formatting guidelines, see:
2+
# https://pip.pypa.io/en/latest/reference/requirements-file-format/
3+
4+
apify < 3.0.0
5+
apify-client
6+
fastapi==0.116.1
7+
httpx>=0.24.0
8+
mcp==1.13.1
9+
pydantic>=2.0.0
10+
sse-starlette>=3.0.2
11+
uv>=0.7.8
12+
uvicorn>=0.27.0

time-mcp-server/src/__init__.py

Whitespace-only changes.

time-mcp-server/src/__main__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import asyncio
2+
3+
from .main import main
4+
5+
# Execute the Actor entry point.
6+
asyncio.run(main())

time-mcp-server/src/const.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from enum import Enum
2+
3+
4+
class ChargeEvents(str, Enum):
5+
"""Event types for charging MCP operations (time-mcp-server)."""
6+
7+
# Generic MCP operations
8+
ACTOR_START = 'actor-start'
9+
RESOURCE_READ = 'resource-read'
10+
TOOL_LIST = 'tool-list'
11+
PROMPT_GET = 'prompt-get'
12+
TOOL_CALL = 'tool-call'
13+
14+
# Time MCP-specific events
15+
GET_CURRENT_TIME = 'get_current_time'
16+
CONVERT_TIME = 'convert_time'
17+
18+
19+
# Tool whitelist for MCP server
20+
# Only tools listed here will be present to the user and allowed to execute.
21+
# Format of the dictionary: {tool_name: (charge_event_name, default_count)}
22+
# To add new authorized tools, add an entry with the tool name and its charging configuration.
23+
TOOL_WHITELIST = {
24+
ChargeEvents.GET_CURRENT_TIME.value: (ChargeEvents.GET_CURRENT_TIME.value, 1),
25+
ChargeEvents.CONVERT_TIME.value: (ChargeEvents.CONVERT_TIME.value, 1),
26+
}
27+
28+
# PPE (Pay Per Event) mapping for Apify monetization
29+
BILLABLE_EVENTS = {
30+
'get_current_time': {'price': 0.0005},
31+
'convert_time': {'price': 0.0005},
32+
}

0 commit comments

Comments
 (0)