Jofthomas commited on
Commit
1d54ed8
·
1 Parent(s): 80dc3a4

basic example

Browse files
Files changed (1) hide show
  1. echo_server.py +27 -118
echo_server.py CHANGED
@@ -1,127 +1,36 @@
1
- from mcp.server.fastmcp import FastMCP
2
- from typing import Dict, Any, Optional
3
- from contextvars import ContextVar
4
- from starlette.middleware.base import BaseHTTPMiddleware
5
- from starlette.requests import Request
6
- from fastmcp.server.middleware import Middleware, MiddlewareContext
7
- import requests
8
 
9
- mcp = FastMCP(name="OpenWeatherServer", stateless_http=True)
10
 
11
- OPENWEATHER_BASE_URL = "https://api.openweathermap.org/data/2.5"
12
 
13
- # Context variable to hold the token for the current request
14
- _current_token: ContextVar[Optional[str]] = ContextVar("openweather_token", default=None)
 
 
 
 
15
 
16
 
17
- class AuthorizationHeaderMiddleware(BaseHTTPMiddleware):
18
- async def dispatch(self, request: Request, call_next):
19
- auth_header = request.headers.get("Authorization", "").strip()
20
- token: Optional[str] = None
21
- if auth_header:
22
- lower = auth_header.lower()
23
- if lower.startswith("bearer "):
24
- token = auth_header[7:].strip()
25
- else:
26
- token = auth_header
27
- _current_token.set(token)
28
- response = await call_next(request)
29
- return response
30
 
 
 
 
31
 
32
- class RequireAuthMiddleware(Middleware):
33
- async def on_request(self, context: MiddlewareContext, call_next):
34
- # Enforce a token is present for any MCP request (tool calls, listings, etc.)
35
- token = _current_token.get()
36
- if not token:
37
- raise ValueError(
38
- "Missing OpenWeather API key. Provide it as a Bearer token in the Authorization header."
39
- )
40
- return await call_next(context)
41
 
42
 
43
- # Register FastMCP middleware
44
- mcp.add_middleware(RequireAuthMiddleware())
45
-
46
-
47
- def _require_api_key_from_header() -> str:
48
- token = _current_token.get()
49
- if not token:
50
- raise ValueError(
51
- "Missing OpenWeather API key. Provide it as a Bearer token in the Authorization header."
52
- )
53
- return token
54
-
55
-
56
- def _request(path: str, params: Dict[str, Any]) -> Dict[str, Any]:
57
- url = f"{OPENWEATHER_BASE_URL}/{path}"
58
- try:
59
- response = requests.get(url, params=params, timeout=15)
60
- except requests.RequestException as exc:
61
- raise RuntimeError(f"Failed to call OpenWeather: {exc}") from exc
62
-
63
- if response.status_code != 200:
64
- try:
65
- payload = response.json()
66
- except Exception:
67
- payload = {"message": response.text}
68
- message = payload.get("message") or payload
69
- raise ValueError(f"OpenWeather error {response.status_code}: {message}")
70
-
71
- return response.json()
72
-
73
-
74
- @mcp.tool(description="Get current weather by city name. Token must be in Authorization: Bearer <token> header.")
75
- def get_current_weather_city(
76
- city: str,
77
- country_code: str = "",
78
- units: str = "metric",
79
- lang: str = "en",
80
- ) -> Dict[str, Any]:
81
- key = _require_api_key_from_header()
82
- q = city if not country_code else f"{city},{country_code}"
83
- params = {"q": q, "appid": key, "units": units, "lang": lang}
84
- return _request("weather", params)
85
-
86
-
87
- @mcp.tool(description="Get current weather by geographic coordinates. Token must be in Authorization header.")
88
- def get_current_weather_coords(
89
- lat: float,
90
- lon: float,
91
- units: str = "metric",
92
- lang: str = "en",
93
- ) -> Dict[str, Any]:
94
- key = _require_api_key_from_header()
95
- params = {"lat": lat, "lon": lon, "appid": key, "units": units, "lang": lang}
96
- return _request("weather", params)
97
-
98
-
99
- @mcp.tool(description="Get 5 day / 3 hour forecast by city. Token must be in Authorization header.")
100
- def get_forecast_city(
101
- city: str,
102
- country_code: str = "",
103
- units: str = "metric",
104
- lang: str = "en",
105
- ) -> Dict[str, Any]:
106
- key = _require_api_key_from_header()
107
- q = city if not country_code else f"{city},{country_code}"
108
- params = {"q": q, "appid": key, "units": units, "lang": lang}
109
- return _request("forecast", params)
110
-
111
-
112
- @mcp.tool(description="Get 5 day / 3 hour forecast by coordinates. Token must be in Authorization header.")
113
- def get_forecast_coords(
114
- lat: float,
115
- lon: float,
116
- units: str = "metric",
117
- lang: str = "en",
118
- ) -> Dict[str, Any]:
119
- key = _require_api_key_from_header()
120
- params = {"lat": lat, "lon": lon, "appid": key, "units": units, "lang": lang}
121
- return _request("forecast", params)
122
-
123
-
124
- def openweather_http_app():
125
- app = mcp.streamable_http_app()
126
- app.add_middleware(AuthorizationHeaderMiddleware)
127
- return app
 
1
+ from fastmcp import FastMCP
2
+ from fastmcp.server.dependencies import get_http_headers
3
+ from loguru import logger
 
 
 
 
4
 
5
+ mcp = FastMCP("My MCP Server")
6
 
 
7
 
8
+ @mcp.tool
9
+ def greet() -> str:
10
+ headers = get_http_headers()
11
+ logger.info(f"Received headers: {headers}")
12
+ name = headers.get("x-given-name", "unknown")
13
+ return f"Hello, {name}!"
14
 
15
 
16
+ @mcp.tool
17
+ async def safe_header_info() -> dict:
18
+ """Safely get header information without raising errors."""
19
+ # Get headers (returns empty dict if no request context)
20
+ headers = get_http_headers()
 
 
 
 
 
 
 
 
21
 
22
+ # Get authorization header
23
+ auth_header = headers.get("authorization", "")
24
+ is_bearer = auth_header.startswith("Bearer ")
25
 
26
+ return {
27
+ "user_agent": headers.get("user-agent", "Unknown"),
28
+ "content_type": headers.get("content-type", "Unknown"),
29
+ "has_auth": bool(auth_header),
30
+ "auth_type": "Bearer" if is_bearer else "Other" if auth_header else "None",
31
+ "headers_count": len(headers),
32
+ }
 
 
33
 
34
 
35
+ if __name__ == "__main__":
36
+ mcp.run(transport="streamable-http", port=8000, host="0.0.0.0")