Files
strava-mcp-server/scripts/generate_test_map.py
T
matthias f0b806a627
CI/CD Pipeline / Lint & Check (push) Successful in 11s
CI/CD Pipeline / Publish to PyPI (push) Successful in 13s
CI/CD Pipeline / Build & Push Docker Image (push) Successful in 1m33s
refactor: return str directly from resources to comply with FastMCP design and fix mimeType mismatch
2026-05-30 19:38:22 +02:00

195 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asyncio
import os
import sys
from dotenv import load_dotenv
# Add src directory to path
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "src"))
from strava_mcp_server.strava_client import StravaClient
from mcp.server.fastmcp import Context
class MockContext(Context):
def __init__(self):
# Initialize parent with dummy request / info
pass
async def info(self, msg):
print(f"️ [INFO] {msg}")
async def warning(self, msg):
print(f"⚠️ [WARN] {msg}")
async def error(self, msg):
print(f"❌ [ERROR] {msg}")
async def generate():
from strava_mcp_server.config import get_config_file
# Load user config path
config_file = get_config_file()
if os.path.exists(config_file):
load_dotenv(dotenv_path=config_file)
# Fallback to local .env
load_dotenv()
client_id = os.getenv("STRAVA_CLIENT_ID")
client_secret = os.getenv("STRAVA_CLIENT_SECRET")
refresh_token = os.getenv("STRAVA_REFRESH_TOKEN")
if not all([client_id, client_secret, refresh_token]):
print(
"️ Missing Strava credentials in .env file. Generating a beautiful MOCK route (Alpe d'Huez, France) for testing!"
)
# A real encoded polyline representing the famous Alpe d'Huez climb in France!
mock_polyline = (
"y~yxFn{_e@_AmAcAaBo@sAGo@Gs@H_ANc@jAyB^kALu@D_@Fk@KqBIkACe@DuABsAd@sBHi@@e@KoAKo@eAsC"
"e@oAMa@Bs@Jc@Nk@bAgBv@wAd@{AJc@Ds@Fk@GqAMmAEc@ByA@uAb@sBDi@@e@KoAKo@eAsCe@oAMa@Bs@Jc@Nk"
"@bAgBv@wAd@{AJc@Ds@Fk@GqAMmAEc@ByA@uAb@sBDi@@e@KoAKo@eAsCe@oAMa@Bs@Jc@Nk@bAgBv@wAd@{"
"AJc@Ds@Fk@GqAMmAEc@ByA@oB`@cCDq@@e@IoAKo@eAsCe@oAMi@FkADqAd@sBHi@@e@KoAKo@eAsCe@oAM"
)
# Import the tool function
from strava_mcp_server.tools.activities import register
class MockMCP:
def __init__(self):
self.tools = {}
self.resources = {}
def tool(self):
def decorator(func):
self.tools[func.__name__] = func
return func
return decorator
def resource(self, path, **kwargs):
def decorator(func):
self.resources[path] = func
return func
return decorator
mcp = MockMCP()
# Mock StravaClient that returns our mock activity and streams
class MockClient:
async def get_activity(self, activity_id):
return {
"name": "🏔️ Alpe d'Huez Climb (Mock Test)",
"map": {"polyline": mock_polyline},
"distance": 14000,
"moving_time": 3600,
"total_elevation_gain": 1110,
"average_speed": 3.88,
"average_heartrate": 165,
"calories": 950,
}
async def get_activity_streams(self, activity_id, keys):
# Generate Alpe d'Huez climbing curve from 740m to 1850m
return [
{"type": "distance", "data": [i * 100 for i in range(141)]},
{
"type": "altitude",
"data": [
740 + int(1110 * (i / 140) ** 0.75) for i in range(141)
],
},
{
"type": "latlng",
"data": [
[45.1105 + (i * 0.0001), 6.0375 + (i * 0.0001)]
for i in range(141)
],
},
]
client = MockClient()
register(mcp, client)
print("Generating mock map HTML from resource...")
resource_handler = mcp.resources["strava://activity/{activity_id}/map"]
html_text = await resource_handler(99999)
if html_text:
output_path = os.path.join(os.path.dirname(__file__), "..", "test.html")
with open(output_path, "w", encoding="utf-8") as f:
f.write(html_text)
print(f"✅ Success! Mock map written to '{os.path.abspath(output_path)}'")
print(
"👉 Double-click 'test.html' or open it in your browser to view the interactive map!"
)
return
client = StravaClient()
print("Fetching recent activities to find one with GPS data...")
try:
activities = await client.list_activities(limit=20)
gps_activity = None
for a in activities:
map_data = a.get("map", {})
if map_data.get("summary_polyline") or map_data.get("polyline"):
gps_activity = a
break
if not gps_activity:
print(
"❌ No activity with GPS/polyline data found in your recent 20 activities."
)
return
activity_id = gps_activity["id"]
print(f"Found GPS Activity: '{gps_activity.get('name')}' (ID: {activity_id})")
# Import the tool function
from strava_mcp_server.tools.activities import register
class MockMCP:
def __init__(self):
self.tools = {}
self.resources = {}
def tool(self):
def decorator(func):
self.tools[func.__name__] = func
return func
return decorator
def resource(self, path, **kwargs):
def decorator(func):
self.resources[path] = func
return func
return decorator
mcp = MockMCP()
register(mcp, client)
print("Generating map HTML from resource...")
resource_handler = mcp.resources["strava://activity/{activity_id}/map"]
html_text = await resource_handler(activity_id)
if html_text:
output_path = os.path.join(os.path.dirname(__file__), "..", "test.html")
with open(output_path, "w", encoding="utf-8") as f:
f.write(html_text)
print(f"✅ Success! Map written to '{os.path.abspath(output_path)}'")
print(
"👉 Double-click 'test.html' or open it in your browser to view the interactive map!"
)
except Exception as e:
print(f"❌ Failed: {str(e)}")
if __name__ == "__main__":
asyncio.run(generate())