diff --git a/src/strava_mcp_server/__init__.py b/src/strava_mcp_server/__init__.py index c0a5e1a..b5bc102 100644 --- a/src/strava_mcp_server/__init__.py +++ b/src/strava_mcp_server/__init__.py @@ -1,3 +1,3 @@ """Strava MCP Server""" -__version__ = "0.2.11" +__version__ = "0.2.12" diff --git a/src/strava_mcp_server/tools/activities.py b/src/strava_mcp_server/tools/activities.py index c3f54cc..fbd01bd 100644 --- a/src/strava_mcp_server/tools/activities.py +++ b/src/strava_mcp_server/tools/activities.py @@ -931,3 +931,20 @@ def register(mcp: FastMCP, strava: StravaClient) -> None: return await _render_activity_map_html(last_id) except Exception as e: return f"
Error loading last activity: {str(e)}
" + + from starlette.requests import Request + from starlette.responses import HTMLResponse + + @mcp.custom_route("/get-map/{activity_id}", methods=["GET"]) + async def get_web_map(request: Request) -> HTMLResponse: + """HTTP Endpoint to directly serve the interactive Leaflet HTML map.""" + activity_id_str = request.path_params.get("activity_id") + try: + activity_id = int(activity_id_str) + html_content = await _render_activity_map_html(activity_id) + return HTMLResponse(content=html_content) + except Exception as e: + return HTMLResponse( + content=f"{str(e)}
", + status_code=400, + ) diff --git a/tests/unit/test_map.py b/tests/unit/test_map.py index eedd82b..7147b29 100644 --- a/tests/unit/test_map.py +++ b/tests/unit/test_map.py @@ -25,6 +25,16 @@ class MockMCP: return decorator + def custom_route(self, path, methods, **kwargs): + if not hasattr(self, "routes"): + self.routes = {} + + def decorator(func): + self.routes[path] = func + return func + + return decorator + @pytest.mark.asyncio async def test_get_activity_map_success(): @@ -109,3 +119,45 @@ async def test_get_activity_map_no_polyline(): assert len(result.content) == 1 assert result.content[0].type == "text" assert "⚠️ Keine GPS-Routen-Daten" in result.content[0].text + + +@pytest.mark.asyncio +async def test_get_web_map_route_success(): + mcp = MockMCP() + strava = MagicMock() + + # Mock detailed activity with a valid polyline + strava.get_activity = AsyncMock( + return_value={ + "name": "Evening Ride", + "map": {"polyline": "a~l~FqzbwOq}@it@..."}, + "sport_type": "Ride", + } + ) + strava.get_activity_streams = AsyncMock( + return_value={ + "altitude": {"data": [100.0, 101.0, 102.0]}, + "distance": {"data": [0.0, 100.0, 200.0]}, + "latlng": {"data": [[47.1, 9.1], [47.2, 9.2], [47.3, 9.3]]}, + } + ) + + register(mcp, strava) + + assert "/get-map/{activity_id}" in mcp.routes + route_handler = mcp.routes["/get-map/{activity_id}"] + + # Mock Starlette Request + request = MagicMock() + request.path_params = {"activity_id": "12345"} + + response = await route_handler(request) + + from starlette.responses import HTMLResponse + + assert isinstance(response, HTMLResponse) + assert response.status_code == 200 + + html_body = response.body.decode("utf-8") + assert "" in html_body + assert "Evening Ride" in html_body