first commit
This commit is contained in:
commit
61278b132a
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
__pycache__
|
70
README.md
Normal file
70
README.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Hong Kong Observatory MCP Server
|
||||
|
||||
A Model Context Protocol (MCP) server that provides weather information of Hong Kong Observatory.
|
||||
|
||||
## Features
|
||||
|
||||
- Weather information (forecasts, warnings, current conditions)
|
||||
- Earthquake information
|
||||
- Gregorian to Lunar calendar conversion
|
||||
- Rainfall data from monitoring stations
|
||||
|
||||
## Data Source
|
||||
This project utilizes the official HKO Open Data API:
|
||||
- Weather Information base URL: https://data.weather.gov.hk/weatherAPI/opendata/weather.php
|
||||
- Earthquake Information base URL: https://data.weather.gov.hk/weatherAPI/opendata/earthquake.php
|
||||
- Gregorian-Lunar Calendar Conversion base URL: https://data.weather.gov.hk/weatherAPI/opendata/lunardate.php
|
||||
- Rainfall in the Past Hour from Automatic Weather Station base URL: https://data.weather.gov.hk/weatherAPI/opendata/hourlyRainfall.php
|
||||
- [HKO Open Data API Documentation](https://www.hko.gov.hk/en/weatherAPI/doc/files/HKO_Open_Data_API_Documentation.pdf)
|
||||
|
||||
## Prerequisites
|
||||
- Python 3.10 or higher
|
||||
|
||||
## Installation
|
||||
1. Install Python
|
||||
|
||||
2. Clone this repository:
|
||||
```bash
|
||||
git clone https://github.com/pongiotdevelop/hko_mcp.git
|
||||
```
|
||||
|
||||
## Usage
|
||||
1. Add the below to your MCP configuration:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"HKO_MCP": {
|
||||
"command": "python",
|
||||
"args": [
|
||||
"<directory of the project>"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. The server provides several tools that can be used by Language Models to query HKO information:
|
||||
- `get_weather_info(dataType: str, lang: str = "en")`: Get weather information from the Hong Kong Observatory.
|
||||
- `get_earthquake_info(dataType: str, lang: str = "en")`: Get earthquake information from the Hong Kong Observatory.
|
||||
- `Gregorian_Lunar_Calendar_Conversion(date: str)`: Convert Gregorian date to Lunar date.
|
||||
- `rainfall_past_hour_from_station(lang: str = "en")`: Get rainfall past hour from the Hong Kong Observatory.
|
||||
|
||||
## Testing
|
||||
Run the test.py for testing the API functions of MCP server:
|
||||
```bash
|
||||
python test.py
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
- `requests`: For HTTP requests
|
||||
- `fastmcp`: For MCP server implementation
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Hong Kong Observatory for providing the open data API
|
||||
- The MCP protocol developers
|
||||
|
112
hko_mcp.py
Normal file
112
hko_mcp.py
Normal file
@ -0,0 +1,112 @@
|
||||
import requests
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("hko mcp")
|
||||
|
||||
@mcp.tool()
|
||||
def get_weather_info(dataType: str, lang: str = "en"):
|
||||
"""
|
||||
Get weather information from the Hong Kong Observatory.
|
||||
|
||||
Args:
|
||||
dataType: The type of weather information to get.
|
||||
- flw: Local Weather Forecast
|
||||
- fnd: 9-day Weather Forecast
|
||||
- rhrread: Current Weather Report
|
||||
- warnsum: Weather Warning Summary
|
||||
- warningInfo: Weather Warning Information
|
||||
- swt: Special Weather Tips
|
||||
lang: The language of the weather information.
|
||||
- en: English
|
||||
- tc: Traditional Chinese
|
||||
- sc: Simplified Chinese
|
||||
Returns:
|
||||
The weather information by JSON format.
|
||||
"""
|
||||
|
||||
# http get request
|
||||
# url = f"https://www.hko.gov.hk/wxinfo/fnd/fnd_{lang}.htm"
|
||||
url = f"https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType={dataType}&lang={lang}"
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
html = response.text
|
||||
# print(html) # for check the response only
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_earthquake_info(dataType: str, lang: str = "en"):
|
||||
"""
|
||||
Get earthquake information from the Hong Kong Observatory.
|
||||
|
||||
Args:
|
||||
dataType: The type of earthquake information to get.
|
||||
- qem: Quick Earthquake Messages
|
||||
- feltearthquake: Locally Felt Earth Tremor Report
|
||||
lang: The language of the earthquake information.
|
||||
- en: English
|
||||
- tc: Traditional Chinese
|
||||
- sc: Simplified Chinese
|
||||
Returns:
|
||||
The earthquake information by JSON format.
|
||||
"""
|
||||
|
||||
url = f"https://data.weather.gov.hk/weatherAPI/opendata/earthquake.php?dataType={dataType}&lang={lang}"
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
html = response.text
|
||||
# print(html) # for check the response only
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def Gregorian_Lunar_Calendar_Coversion(date: str):
|
||||
"""
|
||||
Convert Gregorian date to Lunar date.
|
||||
|
||||
Args:
|
||||
date: The Gregorian date to convert.
|
||||
- Format: YYYY-MM-DD
|
||||
Returns:
|
||||
The Lunar date in by JSON format.
|
||||
Args:
|
||||
LunarYear: The Lunar year.
|
||||
LunarDate: The Lunar date.
|
||||
"""
|
||||
|
||||
url = f"https://data.weather.gov.hk/weatherAPI/opendata/lunardate.php??date={date}"
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
html = response.text
|
||||
# print(html) # for check the response only
|
||||
|
||||
return html
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def rainfall_past_hour_from_station(lang: str = "en"):
|
||||
"""
|
||||
Get rainfall past hour from the Hong Kong Observatory.
|
||||
|
||||
Args:
|
||||
lang: The language of the rainfall information.
|
||||
- en: English
|
||||
- tc: Traditional Chinese
|
||||
- sc: Simplified Chinese
|
||||
Returns:
|
||||
The rainfall past hour information by JSON format.
|
||||
"""
|
||||
|
||||
url = f"https://data.weather.gov.hk/weatherAPI/opendata/hourlyRainfall.php?lang={lang}"
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
html = response.text
|
||||
# print(html) # for check the response only
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
mcp.run(transport='stdio')
|
100
test.py
Normal file
100
test.py
Normal file
@ -0,0 +1,100 @@
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
import json
|
||||
from hko_mcp import get_weather_info, get_earthquake_info, Gregorian_Lunar_Calendar_Coversion, rainfall_past_hour_from_station
|
||||
|
||||
class TestHKOMCP(unittest.TestCase):
|
||||
|
||||
@patch('hko_mcp.requests.get')
|
||||
def test_get_weather_info(self, mock_get):
|
||||
# Setup mock response for different data types
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"forecast": "Sunny"}
|
||||
mock_response.text = json.dumps({"forecast": "Sunny"})
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Test with different data types
|
||||
data_types = ["flw", "fnd", "rhrread", "warnsum", "warningInfo", "swt"]
|
||||
languages = ["en", "tc", "sc"]
|
||||
|
||||
for data_type in data_types:
|
||||
for lang in languages:
|
||||
result = get_weather_info(data_type, lang)
|
||||
self.assertEqual(result, {"forecast": "Sunny"})
|
||||
mock_get.assert_called_with(
|
||||
f"https://data.weather.gov.hk/weatherAPI/opendata/weather.php?dataType={data_type}&lang={lang}"
|
||||
)
|
||||
|
||||
@patch('hko_mcp.requests.get')
|
||||
def test_get_earthquake_info(self, mock_get):
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"earthquake": "detected"}
|
||||
mock_response.text = json.dumps({"earthquake": "detected"})
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Test with different data types
|
||||
data_types = ["qem", "feltearthquake"]
|
||||
languages = ["en", "tc", "sc"]
|
||||
|
||||
for data_type in data_types:
|
||||
for lang in languages:
|
||||
result = get_earthquake_info(data_type, lang)
|
||||
self.assertEqual(result, {"earthquake": "detected"})
|
||||
mock_get.assert_called_with(
|
||||
f"https://data.weather.gov.hk/weatherAPI/opendata/earthquake.php?dataType={data_type}&lang={lang}"
|
||||
)
|
||||
|
||||
@patch('hko_mcp.requests.get')
|
||||
def test_gregorian_lunar_calendar_conversion(self, mock_get):
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.text = json.dumps({"LunarYear": 2023, "LunarDate": "8-16"})
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Test with a sample date
|
||||
date = "2023-10-01"
|
||||
result = Gregorian_Lunar_Calendar_Coversion(date)
|
||||
self.assertEqual(result, json.dumps({"LunarYear": 2023, "LunarDate": "8-16"}))
|
||||
mock_get.assert_called_with(
|
||||
f"https://data.weather.gov.hk/weatherAPI/opendata/lunardate.php??date={date}"
|
||||
)
|
||||
|
||||
@patch('hko_mcp.requests.get')
|
||||
def test_rainfall_past_hour_from_station(self, mock_get):
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"rainfall": {"Central": 0.5}}
|
||||
mock_response.text = json.dumps({"rainfall": {"Central": 0.5}})
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Test with different languages
|
||||
languages = ["en", "tc", "sc"]
|
||||
|
||||
for lang in languages:
|
||||
result = rainfall_past_hour_from_station(lang)
|
||||
self.assertEqual(result, {"rainfall": {"Central": 0.5}})
|
||||
mock_get.assert_called_with(
|
||||
f"https://data.weather.gov.hk/weatherAPI/opendata/hourlyRainfall.php?lang={lang}"
|
||||
)
|
||||
|
||||
@patch('hko_mcp.requests.get')
|
||||
def test_error_handling(self, mock_get):
|
||||
# Setup mock response to raise an exception
|
||||
mock_get.side_effect = Exception("API Error")
|
||||
|
||||
# Test error handling for each function
|
||||
with self.assertRaises(Exception):
|
||||
get_weather_info("flw", "en")
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
get_earthquake_info("qem", "en")
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
Gregorian_Lunar_Calendar_Coversion("2023-10-01")
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
rainfall_past_hour_from_station("en")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user