fix: Fix symbol_get/upload/delete handlers for correct API paths

- get_symbol: URL was missing '/raw' suffix + tried .json() on binary SVG
  → now returns download URL + curl command (like download_capture_file)
- upload_symbol: URL was missing '/raw' suffix
  → now accepts SVG content string and POSTs to correct path
- delete_symbol: returns 204 No Content, .json() would fail
  → removed .json() call (just returns message)
This commit is contained in:
YueGuobin 2026-06-10 23:01:27 +08:00
parent d431ff6eaa
commit 80e64ebbae
No known key found for this signature in database
2 changed files with 18 additions and 9 deletions

View File

@ -1037,7 +1037,7 @@ async def symbol_list() -> list[dict[str, Any]]:
async def symbol_get(
symbol_id: Annotated[str, Field(description="Symbol ID (e.g. ':/symbols/router.svg')")],
) -> list[dict[str, Any]]:
"""Get details about a specific symbol."""
"""Get a download URL for a symbol file (SVG). Use curl to download."""
return await asyncio.to_thread(_run_handler_sync, get_symbol_handler, {
"symbol_id": symbol_id,
})
@ -1062,10 +1062,11 @@ async def symbol_defaults() -> list[dict[str, Any]]:
@mcp.tool()
async def symbol_upload(
symbol_id: Annotated[str, Field(description="Symbol ID to upload (e.g. ':/symbols/my_symbol.svg')")],
content: Annotated[str, Field(description="SVG content of the symbol")],
) -> list[dict[str, Any]]:
"""Upload or update a custom symbol on the server."""
"""Upload or update a custom symbol on the server. Provide the SVG content as a string."""
return await asyncio.to_thread(_run_handler_sync, upload_symbol_handler, {
"symbol_id": symbol_id,
"symbol_id": symbol_id, "content": content,
})

View File

@ -50,8 +50,14 @@ def get_symbol_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict
symbol_id = params.get("symbol_id")
if not symbol_id:
return {"error": "symbol_id is required"}
conn = _get_connector(gns3_ctx)
return conn.http_call("get", f"{conn.base_url}/symbols/{symbol_id}").json()
download_url = f"{gns3_ctx['server_url']}/v3/symbols/{symbol_id}/raw"
auth_token = gns3_ctx['jwt_token']
return {
"symbol_id": symbol_id,
"download_url": download_url,
"curl_command": f"curl -L -o '{symbol_id.replace(':', '').replace('/', '_')}.svg' -H 'Authorization: Bearer {auth_token}' '{download_url}'",
"note": "Symbol files are SVG images. Use curl to download.",
}
def get_symbol_dimensions_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]:
@ -70,11 +76,13 @@ def get_default_symbols_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]
def upload_symbol_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]:
symbol_id = params.get("symbol_id")
if not symbol_id:
return {"error": "symbol_id is required"}
content = params.get("content")
if not symbol_id or content is None:
return {"error": "symbol_id and content (SVG data) are required"}
conn = _get_connector(gns3_ctx)
result = conn.http_call("post", f"{conn.base_url}/symbols/{symbol_id}").json()
return {"message": f"Symbol {symbol_id} uploaded", "symbol": result}
url = f"{conn.base_url}/symbols/{symbol_id}/raw"
conn.http_call("post", url, data=content, headers={"Content-Type": "image/svg+xml"})
return {"message": f"Symbol {symbol_id} uploaded", "symbol_id": symbol_id}
def delete_symbol_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: