Jofthomas commited on
Commit
417a718
·
1 Parent(s): a039815
Files changed (2) hide show
  1. echo_server.py +5 -130
  2. server.py +1 -1
echo_server.py CHANGED
@@ -1,134 +1,9 @@
1
  from mcp.server.fastmcp import FastMCP
2
- from typing import Optional, Literal
3
- import httpx
4
- import os
5
- import re
6
- from contextvars import ContextVar
7
 
8
- # Try to import TokenVerifier from fastmcp; fall back gracefully if path differs
9
- try:
10
- from fastmcp.server.auth.verifier import TokenVerifier # type: ignore
11
- except Exception: # pragma: no cover - fallback for alternate installs
12
- TokenVerifier = object # type: ignore
13
 
14
- # Context variable to store the verified bearer token per-request
15
- _current_bearer_token: ContextVar[Optional[str]] = ContextVar("current_bearer_token", default=None)
16
 
17
-
18
- class OpenWeatherTokenVerifier(TokenVerifier): # type: ignore[misc]
19
- """Minimal verifier that accepts an OpenWeather API key as the bearer token.
20
-
21
- For safety, we perform a lightweight format check (32 hex chars typical for OpenWeather keys).
22
- On success, we stash the token in a contextvar and return claims including it.
23
- """
24
-
25
- _hex32 = re.compile(r"^[a-fA-F0-9]{32}$")
26
-
27
- def verify_token(self, token: str) -> dict: # type: ignore[override]
28
- if not token:
29
- raise ValueError("Missing bearer token")
30
- # Basic format validation (doesn't call OpenWeather)
31
- if not self._hex32.match(token):
32
- # Allow override via env to support alternative key formats if needed
33
- allow_any = os.getenv("OPENWEATHER_ALLOW_ANY_TOKEN", "false").lower() in {"1", "true", "yes"}
34
- if not allow_any:
35
- raise ValueError("Invalid OpenWeather token format; expected 32 hex characters")
36
- _current_bearer_token.set(token)
37
- return {"auth_type": "openweather", "openweather_token": token}
38
-
39
-
40
- mcp = FastMCP(name="OpenWeatherServer", stateless_http=True, auth=OpenWeatherTokenVerifier())
41
-
42
- BASE_URL = "https://api.openweathermap.org"
43
- DEFAULT_TIMEOUT_SECONDS = 15.0
44
-
45
-
46
- def _require_token() -> str:
47
- # Priority: verified bearer token → env var → error
48
- token = _current_bearer_token.get()
49
- if token:
50
- return token
51
- env_token = os.getenv("OPENWEATHER_API_KEY")
52
- if env_token:
53
- return env_token
54
- raise ValueError(
55
- "OpenWeather token required. Provide as HTTP Authorization: Bearer <token> or set OPENWEATHER_API_KEY."
56
- )
57
-
58
-
59
- def _get(path: str, params: dict) -> dict:
60
- try:
61
- with httpx.Client(timeout=httpx.Timeout(DEFAULT_TIMEOUT_SECONDS)) as client:
62
- resp = client.get(f"{BASE_URL}{path}", params=params)
63
- resp.raise_for_status()
64
- return resp.json()
65
- except httpx.HTTPStatusError as e:
66
- status = e.response.status_code if e.response is not None else None
67
- text = e.response.text if e.response is not None else str(e)
68
- raise ValueError(f"OpenWeather API error (status={status}): {text}")
69
- except Exception as e:
70
- raise RuntimeError(f"OpenWeather request failed: {e}")
71
-
72
-
73
- @mcp.tool(description="Get current weather using One Call 3.0 (current segment). Token is taken from Authorization bearer or OPENWEATHER_API_KEY env.")
74
- def current_weather(
75
- lat: float,
76
- lon: float,
77
- units: Literal["standard", "metric", "imperial"] = "metric",
78
- lang: str = "en",
79
- ) -> dict:
80
- token = _require_token()
81
- params = {
82
- "lat": lat,
83
- "lon": lon,
84
- "appid": token,
85
- "units": units,
86
- "lang": lang,
87
- "exclude": "minutely,hourly,daily,alerts",
88
- }
89
- data = _get("/data/3.0/onecall", params)
90
- return {
91
- "coord": {"lat": lat, "lon": lon},
92
- "units": units,
93
- "lang": lang,
94
- "current": data.get("current", data),
95
- }
96
-
97
-
98
- @mcp.tool(description="Get One Call 3.0 data for coordinates. Use 'exclude' to omit segments (comma-separated). Token comes from bearer or env.")
99
- def onecall(
100
- lat: float,
101
- lon: float,
102
- exclude: Optional[str] = None,
103
- units: Literal["standard", "metric", "imperial"] = "metric",
104
- lang: str = "en",
105
- ) -> dict:
106
- token = _require_token()
107
- params = {
108
- "lat": lat,
109
- "lon": lon,
110
- "appid": token,
111
- "units": units,
112
- "lang": lang,
113
- }
114
- if exclude:
115
- params["exclude"] = exclude
116
- return _get("/data/3.0/onecall", params)
117
-
118
-
119
- @mcp.tool(description="Get 5-day/3-hour forecast by city name. Token comes from bearer or env.")
120
- def forecast_city(
121
- city: str,
122
- units: Literal["standard", "metric", "imperial"] = "metric",
123
- lang: str = "en",
124
- ) -> dict:
125
- token = _require_token()
126
- params = {"q": city, "appid": token, "units": units, "lang": lang}
127
- return _get("/data/2.5/forecast", params)
128
-
129
-
130
- @mcp.tool(description="Get air pollution data for coordinates. Token comes from bearer or env.")
131
- def air_pollution(lat: float, lon: float) -> dict:
132
- token = _require_token()
133
- params = {"lat": lat, "lon": lon, "appid": token}
134
- return _get("/data/2.5/air_pollution", params)
 
1
  from mcp.server.fastmcp import FastMCP
 
 
 
 
 
2
 
3
+ mcp = FastMCP(name="EchoServer", stateless_http=True)
 
 
 
 
4
 
 
 
5
 
6
+ @mcp.tool(description="A simple echo tool")
7
+ def echo(message: str) -> str:
8
+ return f"Echo: {message}"
9
+ #https://api.openweathermap.org/data/3.0/onecall?lat=33.44&lon=-94.04&appid=dcc70a31bf3f565ddf0d3971d1916f52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
server.py CHANGED
@@ -42,7 +42,7 @@ async def index(request: Request):
42
  app.mount("/echo", echo_mcp.streamable_http_app())
43
  app.mount("/math", math_mcp.streamable_http_app())
44
 
45
- PORT = int(os.environ.get("PORT", "7860"))
46
 
47
  if __name__ == "__main__":
48
  import uvicorn
 
42
  app.mount("/echo", echo_mcp.streamable_http_app())
43
  app.mount("/math", math_mcp.streamable_http_app())
44
 
45
+ PORT = int(os.environ.get("PORT", "10000"))
46
 
47
  if __name__ == "__main__":
48
  import uvicorn