mcp-server-tempest 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_server_tempest/__init__.py +9 -0
- mcp_server_tempest/models.py +464 -0
- mcp_server_tempest/rest.py +25 -0
- mcp_server_tempest/server.py +615 -0
- mcp_server_tempest-0.1.0.dist-info/METADATA +254 -0
- mcp_server_tempest-0.1.0.dist-info/RECORD +9 -0
- mcp_server_tempest-0.1.0.dist-info/WHEEL +4 -0
- mcp_server_tempest-0.1.0.dist-info/entry_points.txt +2 -0
- mcp_server_tempest-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,615 @@
|
|
1
|
+
"""
|
2
|
+
WeatherFlow Tempest MCP Server
|
3
|
+
|
4
|
+
This module provides a Model Context Protocol (MCP) server for accessing WeatherFlow Tempest
|
5
|
+
weather station data. It offers both tools (for interactive queries) and resources
|
6
|
+
(for data access) to retrieve real-time weather observations, forecasts, and station metadata.
|
7
|
+
|
8
|
+
Features:
|
9
|
+
- Real-time weather observations from personal weather stations
|
10
|
+
- Weather forecasts and current conditions
|
11
|
+
- Station and device metadata
|
12
|
+
- Automatic caching with configurable TTL
|
13
|
+
- Support for multiple stations per user account
|
14
|
+
|
15
|
+
Setup:
|
16
|
+
1. Get an API token from https://tempestwx.com/settings/tokens
|
17
|
+
2. Set the WEATHERFLOW_API_TOKEN environment variable
|
18
|
+
3. Run the server: python -m weatherflow_mcp
|
19
|
+
|
20
|
+
Environment Variables:
|
21
|
+
WEATHERFLOW_API_TOKEN: Your WeatherFlow API token (required)
|
22
|
+
WEATHERFLOW_CACHE_TTL: Cache timeout in seconds (default: 300)
|
23
|
+
WEATHERFLOW_CACHE_SIZE: Maximum cache entries (default: 100)
|
24
|
+
|
25
|
+
Example Usage:
|
26
|
+
# Get available stations
|
27
|
+
stations = await client.call_tool("get_stations")
|
28
|
+
|
29
|
+
# Get current conditions for a specific station
|
30
|
+
conditions = await client.call_tool("get_observation", {"station_id": 12345})
|
31
|
+
|
32
|
+
# Access via resources
|
33
|
+
forecast = await client.read_resource("weather://tempest/forecast/12345")
|
34
|
+
"""
|
35
|
+
|
36
|
+
import os
|
37
|
+
from typing import Annotated, Any, Dict
|
38
|
+
|
39
|
+
from cachetools import TTLCache
|
40
|
+
from fastmcp import Context, FastMCP
|
41
|
+
from fastmcp.exceptions import ToolError
|
42
|
+
from pydantic import Field
|
43
|
+
|
44
|
+
from .models import (
|
45
|
+
ForecastResponse,
|
46
|
+
ObservationResponse,
|
47
|
+
StationResponse,
|
48
|
+
StationsResponse,
|
49
|
+
)
|
50
|
+
|
51
|
+
from .rest import (
|
52
|
+
api_get_forecast,
|
53
|
+
api_get_observation,
|
54
|
+
api_get_station_id,
|
55
|
+
api_get_stations,
|
56
|
+
)
|
57
|
+
|
58
|
+
cache = TTLCache(
|
59
|
+
maxsize=os.getenv("WEATHERFLOW_CACHE_SIZE", 100),
|
60
|
+
ttl=os.getenv("WEATHERFLOW_CACHE_TTL", 300),
|
61
|
+
)
|
62
|
+
|
63
|
+
# Create the MCP server
|
64
|
+
mcp = FastMCP(
|
65
|
+
name="WeatherFlow Tempest API Server",
|
66
|
+
instructions="""
|
67
|
+
WeatherFlow Tempest weather station data server.
|
68
|
+
|
69
|
+
🚀 Quick Start:
|
70
|
+
1. Use get_stations() to see your available weather stations
|
71
|
+
2. Use get_observation(station_id) to get current conditions
|
72
|
+
3. Use get_forecast(station_id) to get weather forecasts
|
73
|
+
|
74
|
+
💡 Pro Tips:
|
75
|
+
- Data is cached for 5 minutes to improve performance
|
76
|
+
- All measurements are in the units configured for each station
|
77
|
+
- Use the 'units' or 'station_units' fields to understand the unit system
|
78
|
+
- Station IDs are found in the get_stations() response
|
79
|
+
|
80
|
+
🔧 Available Tools:
|
81
|
+
- get_stations(): List your weather stations
|
82
|
+
- get_observation(station_id): Current weather conditions
|
83
|
+
- get_forecast(station_id): Weather forecast
|
84
|
+
- get_station_id(station_id): Station details and devices
|
85
|
+
- clear_cache(): Clear the data cache (for testing)
|
86
|
+
|
87
|
+
📊 Resource URIs:
|
88
|
+
- weather://tempest/stations - List all stations
|
89
|
+
- weather://tempest/observations/{station_id} - Current conditions
|
90
|
+
- weather://tempest/forecast/{station_id} - Weather forecast
|
91
|
+
- weather://tempest/help - Server documentation
|
92
|
+
|
93
|
+
🔑 Setup: Set WEATHERFLOW_API_TOKEN environment variable
|
94
|
+
Get your token at: https://tempestwx.com/settings/tokens
|
95
|
+
""",
|
96
|
+
)
|
97
|
+
|
98
|
+
|
99
|
+
async def _get_api_token(env_var: str = "WEATHERFLOW_API_TOKEN") -> str:
|
100
|
+
if not (token := os.getenv(env_var)):
|
101
|
+
raise ToolError(
|
102
|
+
f"WeatherFlow API token not configured. Please set the {env_var} environment variable. "
|
103
|
+
f"You can get an API token from https://tempestwx.com/settings/tokens"
|
104
|
+
)
|
105
|
+
return token
|
106
|
+
|
107
|
+
|
108
|
+
async def _get_stations_data(ctx: Context, use_cache: bool = True) -> StationsResponse:
|
109
|
+
"""Shared logic for getting stations data."""
|
110
|
+
token = await _get_api_token()
|
111
|
+
|
112
|
+
if use_cache and "stations" in cache:
|
113
|
+
await ctx.info("Using cached station data")
|
114
|
+
return cache["stations"]
|
115
|
+
|
116
|
+
await ctx.info("Getting available stations via the Tempest API")
|
117
|
+
result = await api_get_stations(token)
|
118
|
+
cache["stations"] = StationsResponse(**result)
|
119
|
+
return cache["stations"]
|
120
|
+
|
121
|
+
|
122
|
+
async def _get_station_id_data(
|
123
|
+
station_id: int, ctx: Context, use_cache: bool = True
|
124
|
+
) -> StationResponse:
|
125
|
+
"""Shared logic for getting station ID data."""
|
126
|
+
token = await _get_api_token()
|
127
|
+
|
128
|
+
cache_id = f"station_id_{station_id}"
|
129
|
+
|
130
|
+
if use_cache and cache_id in cache:
|
131
|
+
await ctx.info(f"Using cached station data for station {station_id}")
|
132
|
+
return cache[cache_id]
|
133
|
+
|
134
|
+
await ctx.info(
|
135
|
+
f"Getting station ID data for station {station_id} via the Tempest API"
|
136
|
+
)
|
137
|
+
result = await api_get_station_id(station_id, token)
|
138
|
+
cache[cache_id] = StationResponse(**result)
|
139
|
+
return cache[cache_id]
|
140
|
+
|
141
|
+
|
142
|
+
async def _get_forecast_data(
|
143
|
+
station_id: int, ctx: Context, use_cache: bool = True
|
144
|
+
) -> ForecastResponse:
|
145
|
+
"""Shared logic for getting forecast data."""
|
146
|
+
token = await _get_api_token()
|
147
|
+
|
148
|
+
cache_id = f"forecast_{station_id}"
|
149
|
+
if use_cache and cache_id in cache:
|
150
|
+
await ctx.info(f"Using cached forecast data for station {station_id}")
|
151
|
+
return cache[cache_id]
|
152
|
+
|
153
|
+
await ctx.info(f"Getting forecast for station {station_id} via the Tempest API")
|
154
|
+
result = await api_get_forecast(station_id, token)
|
155
|
+
cache[cache_id] = ForecastResponse(**result)
|
156
|
+
return cache[cache_id]
|
157
|
+
|
158
|
+
|
159
|
+
async def _get_observation_data(
|
160
|
+
station_id: int, ctx: Context, use_cache: bool = True
|
161
|
+
) -> ObservationResponse:
|
162
|
+
"""Shared logic for getting observation data."""
|
163
|
+
token = await _get_api_token()
|
164
|
+
|
165
|
+
cache_id = f"observation_{station_id}"
|
166
|
+
if use_cache and cache_id in cache:
|
167
|
+
await ctx.info(f"Using cached observation data for station {station_id}")
|
168
|
+
return cache[cache_id]
|
169
|
+
|
170
|
+
await ctx.info(f"Getting observations for station {station_id} via the Tempest API")
|
171
|
+
result = await api_get_observation(station_id, token)
|
172
|
+
cache[cache_id] = ObservationResponse(**result)
|
173
|
+
return cache[cache_id]
|
174
|
+
|
175
|
+
|
176
|
+
@mcp.tool(
|
177
|
+
annotations={
|
178
|
+
"title": "Get Weather Stations",
|
179
|
+
"readOnlyHint": True,
|
180
|
+
"openWorldHint": True,
|
181
|
+
"idempotentHint": False,
|
182
|
+
}
|
183
|
+
)
|
184
|
+
async def get_stations(
|
185
|
+
use_cache: Annotated[
|
186
|
+
bool,
|
187
|
+
Field(
|
188
|
+
default=True,
|
189
|
+
description="Whether to use the cache to store the results of the request (default: True)",
|
190
|
+
),
|
191
|
+
],
|
192
|
+
ctx: Context = None,
|
193
|
+
) -> StationsResponse:
|
194
|
+
"""Get a list of all weather stations accessible with your API token.
|
195
|
+
|
196
|
+
This is typically the first function you should call to discover what weather
|
197
|
+
stations are available to you. Each station contains one or more devices that
|
198
|
+
collect different types of weather data.
|
199
|
+
|
200
|
+
The response includes comprehensive information about each station:
|
201
|
+
- Station metadata (name, location, timezone, elevation)
|
202
|
+
- Connected devices and their capabilities
|
203
|
+
- Device status and last communication times
|
204
|
+
- Station configuration and settings
|
205
|
+
|
206
|
+
**Device Types:**
|
207
|
+
- **Tempest**: All-in-one weather sensor (wind, rain, temperature, etc.)
|
208
|
+
- **Air**: Temperature, humidity, pressure, lightning detection
|
209
|
+
- **Sky**: Wind, rain, solar radiation, UV index
|
210
|
+
- **Hub**: Communication hub for other devices
|
211
|
+
|
212
|
+
**Active vs Inactive Devices:**
|
213
|
+
Devices with a `serial_number` are active and collecting data.
|
214
|
+
Devices without a `serial_number` are no longer active or have been removed.
|
215
|
+
|
216
|
+
Args:
|
217
|
+
use_cache: Whether to use cached station data. Since station configurations
|
218
|
+
rarely change, caching improves performance and reduces API calls.
|
219
|
+
Cache expires after 5 minutes.
|
220
|
+
|
221
|
+
Returns:
|
222
|
+
StationsResponse containing:
|
223
|
+
- List of stations with metadata and device information
|
224
|
+
- API status and response metadata
|
225
|
+
- Station-specific settings like units and location data
|
226
|
+
|
227
|
+
Raises:
|
228
|
+
ToolError: If API token is invalid, network request fails, or you have
|
229
|
+
no accessible stations
|
230
|
+
|
231
|
+
Example Usage:
|
232
|
+
>>> stations = await get_stations()
|
233
|
+
>>> for station in stations.stations:
|
234
|
+
>>> print(f"Station: {station.name} (ID: {station.station_id})")
|
235
|
+
>>> print(f"Location: {station.latitude}, {station.longitude}")
|
236
|
+
>>> for device in station.devices:
|
237
|
+
>>> if device.serial_number: # Active device
|
238
|
+
>>> print(f" Device: {device.device_type}")
|
239
|
+
|
240
|
+
Note:
|
241
|
+
Station IDs returned by this function are used in other tools like
|
242
|
+
get_observation(), get_forecast(), and get_station_id().
|
243
|
+
"""
|
244
|
+
|
245
|
+
try:
|
246
|
+
return await _get_stations_data(ctx, use_cache)
|
247
|
+
except Exception as e:
|
248
|
+
raise ToolError(f"Request failed: {str(e)}")
|
249
|
+
|
250
|
+
|
251
|
+
@mcp.tool(
|
252
|
+
annotations={
|
253
|
+
"title": "Get Weather Station Information",
|
254
|
+
"readOnlyHint": True,
|
255
|
+
"openWorldHint": True,
|
256
|
+
"idempotentHint": False,
|
257
|
+
}
|
258
|
+
)
|
259
|
+
async def get_station_id(
|
260
|
+
station_id: Annotated[
|
261
|
+
int, Field(description="The station ID to get information for", gt=0)
|
262
|
+
],
|
263
|
+
use_cache: Annotated[
|
264
|
+
bool,
|
265
|
+
Field(
|
266
|
+
default=True,
|
267
|
+
description="Whether to use the cache to store the results of the request (default: True)",
|
268
|
+
),
|
269
|
+
],
|
270
|
+
ctx: Context = None,
|
271
|
+
) -> StationResponse:
|
272
|
+
"""Get comprehensive details and configuration for a specific weather station.
|
273
|
+
|
274
|
+
This function provides in-depth information about a single weather station,
|
275
|
+
including all connected devices, detailed configuration settings, and
|
276
|
+
operational status. Use this when you need complete station metadata
|
277
|
+
beyond what get_stations() provides.
|
278
|
+
|
279
|
+
**Station Information Includes:**
|
280
|
+
- Complete station metadata (name, location, elevation, timezone)
|
281
|
+
- Detailed device inventory with specifications and status
|
282
|
+
- Station configuration and measurement units
|
283
|
+
- Device communication history and health status
|
284
|
+
- Public/private settings and sharing permissions
|
285
|
+
|
286
|
+
**Device Details Include:**
|
287
|
+
- Device type, model, and firmware version
|
288
|
+
- Serial numbers and hardware revisions
|
289
|
+
- Last communication timestamps
|
290
|
+
- Device-specific settings and capabilities
|
291
|
+
- Calibration and sensor health information
|
292
|
+
|
293
|
+
**Operational Status:**
|
294
|
+
- Online/offline status for each device
|
295
|
+
- Battery levels (for battery-powered devices)
|
296
|
+
- Signal strength and communication quality
|
297
|
+
- Data collection intervals and settings
|
298
|
+
|
299
|
+
Args:
|
300
|
+
station_id: The numeric identifier of the station. Get this from
|
301
|
+
get_stations() or from your WeatherFlow account dashboard.
|
302
|
+
use_cache: Whether to use cached station data. Station configurations
|
303
|
+
change infrequently, so caching improves performance.
|
304
|
+
Cache expires after 5 minutes.
|
305
|
+
|
306
|
+
Returns:
|
307
|
+
StationResponse containing:
|
308
|
+
- Complete station metadata and settings
|
309
|
+
- Detailed device inventory and status
|
310
|
+
- Configuration parameters and unit settings
|
311
|
+
- API response metadata
|
312
|
+
|
313
|
+
Raises:
|
314
|
+
ToolError: If the station ID doesn't exist, you don't have access to it,
|
315
|
+
API token is invalid, or network request fails
|
316
|
+
|
317
|
+
Example Usage:
|
318
|
+
>>> station = await get_station_id(12345)
|
319
|
+
>>> print(f"Station: {station.name}")
|
320
|
+
>>> print(f"Location: {station.latitude}°, {station.longitude}°")
|
321
|
+
>>> print(f"Elevation: {station.station_meta.elevation}m")
|
322
|
+
>>> print(f"Units: {station.station_units}")
|
323
|
+
>>>
|
324
|
+
>>> # Check device status
|
325
|
+
>>> for device in station.devices:
|
326
|
+
>>> if device.serial_number:
|
327
|
+
>>> status = "Online" if device.device_meta else "Offline"
|
328
|
+
>>> print(f" {device.device_type}: {status}")
|
329
|
+
|
330
|
+
Note:
|
331
|
+
Use get_stations() first to discover available station IDs. This function
|
332
|
+
provides more detailed information than the station list overview.
|
333
|
+
"""
|
334
|
+
try:
|
335
|
+
return await _get_station_id_data(station_id, ctx, use_cache)
|
336
|
+
except Exception as e:
|
337
|
+
raise ToolError(f"Request failed: {str(e)}")
|
338
|
+
|
339
|
+
|
340
|
+
@mcp.tool(
|
341
|
+
annotations={
|
342
|
+
"title": "Get Weather Forecast for a Station",
|
343
|
+
"readOnlyHint": True,
|
344
|
+
"openWorldHint": True,
|
345
|
+
"idempotentHint": False,
|
346
|
+
}
|
347
|
+
)
|
348
|
+
async def get_forecast(
|
349
|
+
station_id: Annotated[
|
350
|
+
int, Field(description="The ID of the station to get forecast for", gt=0)
|
351
|
+
],
|
352
|
+
use_cache: Annotated[
|
353
|
+
bool,
|
354
|
+
Field(
|
355
|
+
default=True,
|
356
|
+
description="Whether to use the cache to store the results of the request (default: True)",
|
357
|
+
),
|
358
|
+
],
|
359
|
+
ctx: Context = None,
|
360
|
+
) -> ForecastResponse:
|
361
|
+
"""Get weather forecast and current conditions for a specific weather station.
|
362
|
+
|
363
|
+
This function retrieves comprehensive weather forecast data including current
|
364
|
+
conditions, hourly forecasts, and daily summaries. The forecast combines
|
365
|
+
data from your personal weather station with professional weather models
|
366
|
+
to provide hyper-local predictions.
|
367
|
+
|
368
|
+
**Current Conditions Include:**
|
369
|
+
- Real-time temperature, humidity, and pressure
|
370
|
+
- Wind speed, direction, and gusts
|
371
|
+
- Precipitation rate and accumulation
|
372
|
+
- Solar radiation and UV index
|
373
|
+
- Visibility and weather conditions
|
374
|
+
- "Feels like" temperature and comfort indices
|
375
|
+
|
376
|
+
**Forecast Data Includes:**
|
377
|
+
- Hourly forecasts for the next 24-48 hours
|
378
|
+
- Daily forecasts for the next 7-10 days
|
379
|
+
- Temperature highs and lows
|
380
|
+
- Precipitation probability and amounts
|
381
|
+
- Wind forecasts and weather condition summaries
|
382
|
+
- Sunrise/sunset times and moon phases
|
383
|
+
|
384
|
+
**Data Sources:**
|
385
|
+
The forecast combines your station's real-time observations with
|
386
|
+
professional meteorological models to provide accurate local predictions
|
387
|
+
that account for your specific microclimate and terrain.
|
388
|
+
|
389
|
+
Args:
|
390
|
+
station_id: The numeric identifier of the weather station. Get this from
|
391
|
+
get_stations() or your WeatherFlow account dashboard.
|
392
|
+
use_cache: Whether to use cached forecast data. Forecasts update
|
393
|
+
frequently, but caching for a few minutes improves performance
|
394
|
+
for repeated requests. Cache expires after 5 minutes.
|
395
|
+
|
396
|
+
Returns:
|
397
|
+
ForecastResponse containing:
|
398
|
+
- Current weather conditions and observations
|
399
|
+
- Hourly forecast data for the next 24-48 hours
|
400
|
+
- Daily forecast summaries for the next week
|
401
|
+
- Station location and unit information
|
402
|
+
- Forecast generation timestamp and metadata
|
403
|
+
|
404
|
+
Raises:
|
405
|
+
ToolError: If the station ID doesn't exist, you don't have access to it,
|
406
|
+
API token is invalid, or network request fails
|
407
|
+
|
408
|
+
Example Usage:
|
409
|
+
>>> forecast = await get_forecast(12345)
|
410
|
+
>>>
|
411
|
+
>>> # Current conditions
|
412
|
+
>>> current = forecast.current_conditions
|
413
|
+
>>> print(f"Current: {current.air_temperature}° {forecast.units.units_temp}")
|
414
|
+
>>> print(f"Conditions: {current.conditions}")
|
415
|
+
>>> print(f"Wind: {current.wind_avg} {forecast.units.units_wind}")
|
416
|
+
>>>
|
417
|
+
>>> # Today's forecast
|
418
|
+
>>> today = forecast.forecast.daily[0]
|
419
|
+
>>> print(f"High/Low: {today.air_temp_high}°/{today.air_temp_low}°")
|
420
|
+
>>> print(f"Rain chance: {today.precip_probability}%")
|
421
|
+
>>>
|
422
|
+
>>> # Next few hours
|
423
|
+
>>> for hour in forecast.forecast.hourly[:6]:
|
424
|
+
>>> time = datetime.fromtimestamp(hour.time)
|
425
|
+
>>> print(f"{time.strftime('%H:%M')}: {hour.air_temperature}°")
|
426
|
+
|
427
|
+
Note:
|
428
|
+
All measurements are returned in the units configured for your station.
|
429
|
+
Check the 'units' field in the response to understand the unit system
|
430
|
+
(e.g., Celsius vs Fahrenheit, m/s vs mph for wind speed).
|
431
|
+
"""
|
432
|
+
try:
|
433
|
+
return await _get_forecast_data(station_id, ctx, use_cache)
|
434
|
+
except Exception as e:
|
435
|
+
raise ToolError(f"Request failed: {str(e)}")
|
436
|
+
|
437
|
+
|
438
|
+
@mcp.tool(
|
439
|
+
annotations={
|
440
|
+
"title": "Get Current Weather Observations for a Station",
|
441
|
+
"readOnlyHint": True,
|
442
|
+
"openWorldHint": True,
|
443
|
+
"idempotentHint": False,
|
444
|
+
}
|
445
|
+
)
|
446
|
+
async def get_observation(
|
447
|
+
station_id: Annotated[
|
448
|
+
int, Field(description="The ID of the station to get observations for", gt=0)
|
449
|
+
],
|
450
|
+
use_cache: Annotated[
|
451
|
+
bool,
|
452
|
+
Field(
|
453
|
+
default=True,
|
454
|
+
description="Whether to use the cache to store the results of the request (default: True)",
|
455
|
+
),
|
456
|
+
],
|
457
|
+
ctx: Context = None,
|
458
|
+
) -> ObservationResponse:
|
459
|
+
"""Get the most recent weather observations from a station.
|
460
|
+
|
461
|
+
This function retrieves detailed current weather conditions including:
|
462
|
+
- Temperature, humidity, pressure
|
463
|
+
- Wind speed and direction
|
464
|
+
- Precipitation data
|
465
|
+
- Solar radiation and UV index
|
466
|
+
- Lightning detection data (if available)
|
467
|
+
|
468
|
+
The data is returned in the units configured for the station. Check the
|
469
|
+
'station_units' field in the response to understand the unit system.
|
470
|
+
|
471
|
+
Args:
|
472
|
+
station_id: The numeric ID of the weather station
|
473
|
+
use_cache: Whether to use cached data (recommended for frequent requests)
|
474
|
+
|
475
|
+
Returns:
|
476
|
+
ObservationResponse containing current weather conditions and metadata
|
477
|
+
|
478
|
+
Raises:
|
479
|
+
ToolError: If the station is not accessible or API request fails
|
480
|
+
|
481
|
+
Example:
|
482
|
+
>>> obs = await get_observation(station_id=12345)
|
483
|
+
>>> temp = obs.obs[0]["air_temperature"] # Current temperature
|
484
|
+
>>> units = obs.station_units["units_temp"] # 'c' or 'f'
|
485
|
+
"""
|
486
|
+
|
487
|
+
try:
|
488
|
+
return await _get_observation_data(station_id, ctx, use_cache)
|
489
|
+
except Exception as e:
|
490
|
+
raise ToolError(f"Request failed: {str(e)}")
|
491
|
+
|
492
|
+
|
493
|
+
@mcp.tool(
|
494
|
+
annotations={
|
495
|
+
"title": "Clear the Weather Data Cache",
|
496
|
+
"readOnlyHint": False,
|
497
|
+
"openWorldHint": False,
|
498
|
+
"idempotentHint": True,
|
499
|
+
}
|
500
|
+
)
|
501
|
+
async def clear_cache(ctx: Context = None) -> str:
|
502
|
+
"""Clear the weather data cache (development tool)"""
|
503
|
+
cache.clear()
|
504
|
+
if ctx:
|
505
|
+
await ctx.info("Cache cleared")
|
506
|
+
return "Cache cleared successfully"
|
507
|
+
|
508
|
+
|
509
|
+
@mcp.resource(
|
510
|
+
uri="weather://tempest/stations",
|
511
|
+
name="Get Weather Stations",
|
512
|
+
mime_type="application/json",
|
513
|
+
)
|
514
|
+
async def get_stations_resource(ctx: Context = None) -> StationsResponse:
|
515
|
+
"""Get a list of all your WeatherFlow stations.
|
516
|
+
|
517
|
+
This resource can be used to get a list of all of the configured weather stations that the user has access to, along with all connected devices.
|
518
|
+
Each result contains information about the station, including its name, location, devices, state, and more.
|
519
|
+
A Device wihout a serial_number indicates that Device is no longer active.
|
520
|
+
|
521
|
+
Returns:
|
522
|
+
StationsResponse object containing the list of stations and API status
|
523
|
+
"""
|
524
|
+
try:
|
525
|
+
return await _get_stations_data(ctx, use_cache=True)
|
526
|
+
except Exception as e:
|
527
|
+
raise ToolError(f"Request failed: {str(e)}")
|
528
|
+
|
529
|
+
|
530
|
+
@mcp.resource(
|
531
|
+
uri="weather://tempest/stations/{station_id}",
|
532
|
+
name="GetWeatherStationByID",
|
533
|
+
mime_type="application/json",
|
534
|
+
)
|
535
|
+
async def get_station_id_resource(
|
536
|
+
station_id: Annotated[
|
537
|
+
int,
|
538
|
+
Field(description="The ID of the station to get station information for", gt=0),
|
539
|
+
],
|
540
|
+
ctx: Context = None,
|
541
|
+
) -> StationResponse:
|
542
|
+
"""Get information and devices for a specific weather station
|
543
|
+
|
544
|
+
This resource can be used to get a list of all of the configured weather stations that the user has access to, along with all connected devices.
|
545
|
+
Each result contains information about the station, including its name, location, devices, state, and more.
|
546
|
+
A Device wihout a serial_number indicates that Device is no longer active.
|
547
|
+
|
548
|
+
Args:
|
549
|
+
station_id (int): The ID of the station to get information for
|
550
|
+
|
551
|
+
Returns:
|
552
|
+
StationResponse object containing comprehensive station metadata and device information
|
553
|
+
"""
|
554
|
+
|
555
|
+
try:
|
556
|
+
return await _get_station_id_data(station_id, ctx, use_cache=True)
|
557
|
+
except Exception as e:
|
558
|
+
raise ToolError(f"Request failed: {str(e)}")
|
559
|
+
|
560
|
+
|
561
|
+
@mcp.resource(
|
562
|
+
uri="weather://tempest/forecast/{station_id}",
|
563
|
+
name="GetWeatherForecast",
|
564
|
+
mime_type="application/json",
|
565
|
+
)
|
566
|
+
async def get_forecast_resource(
|
567
|
+
station_id: Annotated[
|
568
|
+
int, Field(description="The ID of the station to get forecast for", gt=0)
|
569
|
+
],
|
570
|
+
ctx: Context = None,
|
571
|
+
) -> Dict[str, Any]:
|
572
|
+
"""Get information and devices for a specific weather station
|
573
|
+
|
574
|
+
This resource allows the user to retrieve the weather forecast from the specified weather station.
|
575
|
+
|
576
|
+
Args:
|
577
|
+
station_id (int): The ID of the station to get information for
|
578
|
+
|
579
|
+
Returns:
|
580
|
+
ForecastResponse object containing the weather forecast and current conditions
|
581
|
+
"""
|
582
|
+
|
583
|
+
try:
|
584
|
+
return await _get_forecast_data(station_id, ctx, use_cache=True)
|
585
|
+
except Exception as e:
|
586
|
+
raise ToolError(f"Request failed: {str(e)}")
|
587
|
+
|
588
|
+
|
589
|
+
@mcp.resource(
|
590
|
+
uri="weather://tempest/observations/{station_id}",
|
591
|
+
name="GetWeatherObservations",
|
592
|
+
mime_type="application/json",
|
593
|
+
)
|
594
|
+
async def get_observation_resource(
|
595
|
+
station_id: Annotated[
|
596
|
+
int, Field(description="The ID of the station to get observations for", gt=0)
|
597
|
+
],
|
598
|
+
ctx: Context = None,
|
599
|
+
) -> Dict[str, Any]:
|
600
|
+
"""Get latest detailed observations for a specific weather station
|
601
|
+
|
602
|
+
This resource allows the user to retrieve the weather forecast from the specified weather station.
|
603
|
+
|
604
|
+
Returns:
|
605
|
+
ObservationResponse object containing the current weather observations and station metadata
|
606
|
+
"""
|
607
|
+
|
608
|
+
try:
|
609
|
+
return await _get_observation_data(station_id, ctx, use_cache=True)
|
610
|
+
except Exception as e:
|
611
|
+
raise ToolError(f"Request failed: {str(e)}")
|
612
|
+
|
613
|
+
|
614
|
+
if __name__ == "__main__":
|
615
|
+
mcp.run()
|