feat(map): add start (🟢) and finish (🏁) route markers
This commit is contained in:
@@ -8,7 +8,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "..", "src"))
|
|||||||
|
|
||||||
from strava_mcp_server.strava_client import StravaClient
|
from strava_mcp_server.strava_client import StravaClient
|
||||||
from mcp.server.fastmcp import Context
|
from mcp.server.fastmcp import Context
|
||||||
from mcp.types import TextContent
|
from mcp.types import TextContent, EmbeddedResource
|
||||||
|
|
||||||
|
|
||||||
class MockContext(Context):
|
class MockContext(Context):
|
||||||
@@ -117,6 +117,12 @@ async def generate():
|
|||||||
if isinstance(item, TextContent) and "<!DOCTYPE html>" in item.text:
|
if isinstance(item, TextContent) and "<!DOCTYPE html>" in item.text:
|
||||||
html_text = item.text
|
html_text = item.text
|
||||||
break
|
break
|
||||||
|
elif (
|
||||||
|
isinstance(item, EmbeddedResource)
|
||||||
|
and item.resource.mimeType == "text/html"
|
||||||
|
):
|
||||||
|
html_text = item.resource.text
|
||||||
|
break
|
||||||
|
|
||||||
if html_text:
|
if html_text:
|
||||||
output_path = os.path.join(os.path.dirname(__file__), "..", "test.html")
|
output_path = os.path.join(os.path.dirname(__file__), "..", "test.html")
|
||||||
@@ -178,6 +184,12 @@ async def generate():
|
|||||||
if isinstance(item, TextContent) and "<!DOCTYPE html>" in item.text:
|
if isinstance(item, TextContent) and "<!DOCTYPE html>" in item.text:
|
||||||
html_text = item.text
|
html_text = item.text
|
||||||
break
|
break
|
||||||
|
elif (
|
||||||
|
isinstance(item, EmbeddedResource)
|
||||||
|
and item.resource.mimeType == "text/html"
|
||||||
|
):
|
||||||
|
html_text = item.resource.text
|
||||||
|
break
|
||||||
|
|
||||||
if html_text:
|
if html_text:
|
||||||
output_path = os.path.join(os.path.dirname(__file__), "..", "test.html")
|
output_path = os.path.join(os.path.dirname(__file__), "..", "test.html")
|
||||||
|
|||||||
@@ -32,6 +32,19 @@ def _user_text(text: str) -> TextContent:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _user_html_resource(uri: str, html: str) -> EmbeddedResource:
|
||||||
|
"""Helper: return a user-facing EmbeddedResource with text/html."""
|
||||||
|
return EmbeddedResource(
|
||||||
|
type="resource",
|
||||||
|
resource=TextResourceContents(
|
||||||
|
uri=uri,
|
||||||
|
mimeType="text/html",
|
||||||
|
text=html,
|
||||||
|
),
|
||||||
|
annotations=Annotations(audience=["user"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def register(mcp: FastMCP, strava: StravaClient) -> None:
|
def register(mcp: FastMCP, strava: StravaClient) -> None:
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
async def list_activities(
|
async def list_activities(
|
||||||
@@ -639,6 +652,23 @@ def register(mcp: FastMCP, strava: StravaClient) -> None:
|
|||||||
if (coordinates.length > 0) {{
|
if (coordinates.length > 0) {{
|
||||||
track = L.polyline(coordinates, {{color: '#fc4c02', weight: 5, opacity: 0.8}}).addTo(map);
|
track = L.polyline(coordinates, {{color: '#fc4c02', weight: 5, opacity: 0.8}}).addTo(map);
|
||||||
map.fitBounds(track.getBounds());
|
map.fitBounds(track.getBounds());
|
||||||
|
|
||||||
|
// Start- und End-Marker zeichnen
|
||||||
|
var startIcon = L.divIcon({{
|
||||||
|
html: '<span style="font-size: 18px; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3)); line-height: 18px; display: block; text-align: center;">🟢</span>',
|
||||||
|
iconSize: [18, 18],
|
||||||
|
iconAnchor: [9, 9],
|
||||||
|
className: 'start-marker-emoji'
|
||||||
|
}});
|
||||||
|
L.marker(coordinates[0], {{ icon: startIcon, zIndexOffset: 500 }}).addTo(map);
|
||||||
|
|
||||||
|
var endIcon = L.divIcon({{
|
||||||
|
html: '<span style="font-size: 22px; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3)); line-height: 22px; display: block; text-align: center;">🏁</span>',
|
||||||
|
iconSize: [22, 22],
|
||||||
|
iconAnchor: [11, 20],
|
||||||
|
className: 'end-marker-emoji'
|
||||||
|
}});
|
||||||
|
L.marker(coordinates[coordinates.length - 1], {{ icon: endIcon, zIndexOffset: 500 }}).addTo(map);
|
||||||
}} else {{
|
}} else {{
|
||||||
map.setView([0, 0], 2);
|
map.setView([0, 0], 2);
|
||||||
}}
|
}}
|
||||||
@@ -841,7 +871,10 @@ def register(mcp: FastMCP, strava: StravaClient) -> None:
|
|||||||
</body>
|
</body>
|
||||||
</html>"""
|
</html>"""
|
||||||
return [
|
return [
|
||||||
TextContent(type="text", text=html_content.strip()),
|
_user_html_resource(
|
||||||
|
f"internal://activities/{activity_id}/map.html",
|
||||||
|
html_content.strip(),
|
||||||
|
),
|
||||||
_resource(
|
_resource(
|
||||||
f"internal://activities/{activity_id}/map",
|
f"internal://activities/{activity_id}/map",
|
||||||
{"polyline": polyline},
|
{"polyline": polyline},
|
||||||
|
|||||||
+16
-10
@@ -47,16 +47,22 @@ async def test_get_activity_map_success():
|
|||||||
result = await get_activity_map(ctx, 12345)
|
result = await get_activity_map(ctx, 12345)
|
||||||
|
|
||||||
assert len(result) == 2
|
assert len(result) == 2
|
||||||
# First block is the HTML text content
|
# First block is the HTML embedded resource
|
||||||
assert result[0].type == "text"
|
assert result[0].type == "resource"
|
||||||
assert "<!DOCTYPE html>" in result[0].text
|
assert result[0].resource.mimeType == "text/html"
|
||||||
assert "a~l~FqzbwOq}@it@..." in result[0].text
|
assert "internal://activities/12345/map.html" in str(result[0].resource.uri)
|
||||||
assert "unpkg.com/leaflet" in result[0].text
|
|
||||||
assert "latlngData" in result[0].text
|
html_text = result[0].resource.text
|
||||||
assert "[47.1, 9.1]" in result[0].text
|
assert "<!DOCTYPE html>" in html_text
|
||||||
assert 'sportEmoji = "🚴"' in result[0].text
|
assert "a~l~FqzbwOq}@it@..." in html_text
|
||||||
assert "L.control.layers" in result[0].text
|
assert "unpkg.com/leaflet" in html_text
|
||||||
assert "satellite" in result[0].text
|
assert "latlngData" in html_text
|
||||||
|
assert "[47.1, 9.1]" in html_text
|
||||||
|
assert 'sportEmoji = "🚴"' in html_text
|
||||||
|
assert "L.control.layers" in html_text
|
||||||
|
assert "satellite" in html_text
|
||||||
|
assert "🟢" in html_text
|
||||||
|
assert "🏁" in html_text
|
||||||
|
|
||||||
# Second block is the internal resource
|
# Second block is the internal resource
|
||||||
assert result[1].type == "resource"
|
assert result[1].type == "resource"
|
||||||
|
|||||||
Reference in New Issue
Block a user