From 9e4edc8a8ac46eac022a5862799f77347377c229 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Tue, 25 Aug 2026 13:22:59 +0800 Subject: [PATCH] chore: disable symbol MCP tools for now MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symbol tools (symbol_list/get/dimensions/defaults/upload/delete) require a vision-capable model to be genuinely useful — they shuttle SVG content, which a text-only LLM cannot inspect or produce. The tool registrations and imports are commented out (handlers stay in symbols.py); revisit when multimodal support is worked out. --- docs/features/mcp-service.md | 17 ++-- gns3server/agent/mcp/__init__.py | 132 ++++++++++++++++--------------- 2 files changed, 75 insertions(+), 74 deletions(-) diff --git a/docs/features/mcp-service.md b/docs/features/mcp-service.md index 3115f1bab..3dbfdbbd4 100644 --- a/docs/features/mcp-service.md +++ b/docs/features/mcp-service.md @@ -94,7 +94,7 @@ All subsequent tool handler REST API calls use this JWT → zero extra bcrypt ## Available Tools -**82 tools** across 12 categories: +**76 tools** across 11 categories: ### Project (14) @@ -191,16 +191,11 @@ All subsequent tool handler REST API calls use this JWT → zero extra bcrypt | `drawing_update` | Update drawing (position, rotation, SVG) | | `drawing_delete` | Delete a drawing | -### Symbol (6) - -| Tool | Description | -|------|-------------| -| `symbol_list` | List all symbols | -| `symbol_get` | Get symbol download URL | -| `symbol_dimensions` | Get symbol dimensions | -| `symbol_defaults` | Get default symbol mapping | -| `symbol_upload` | Upload a custom symbol (SVG content) | -| `symbol_delete` | Delete a custom symbol (built-in: 403) | + ### Appliance (3) diff --git a/gns3server/agent/mcp/__init__.py b/gns3server/agent/mcp/__init__.py index 608070f0f..191d819cf 100644 --- a/gns3server/agent/mcp/__init__.py +++ b/gns3server/agent/mcp/__init__.py @@ -66,11 +66,14 @@ from .projects import ( from .server import ( get_version_handler, get_statistics_handler, ) -from .symbols import ( - get_symbols_handler, get_symbol_handler, - get_symbol_dimensions_handler, get_default_symbols_handler, - upload_symbol_handler, delete_symbol_handler, -) +# Symbol tools are disabled for now: they require a vision-capable model to +# be genuinely useful (the tools shuttle SVG content, which a text-only LLM +# cannot inspect or produce). Revisit later. +# from .symbols import ( +# get_symbols_handler, get_symbol_handler, +# get_symbol_dimensions_handler, get_default_symbols_handler, +# upload_symbol_handler, delete_symbol_handler, +# ) from .appliances import ( get_appliances_handler, get_appliance_handler, install_appliance_handler, @@ -1280,64 +1283,67 @@ async def server_statistics() -> list[dict[str, Any]]: # ── Symbol tools ────────────────────────────────────────────────────── - - -@mcp.tool() -async def symbol_list() -> list[dict[str, Any]]: - """List all available symbols on the server.""" - return await asyncio.to_thread(_run_handler_sync, get_symbols_handler, {}) - - -@mcp.tool() -async def symbol_get( - symbol_id: Annotated[str, Field(description="Symbol ID (e.g. ':/symbols/router.svg')")], -) -> list[dict[str, Any]]: - """Get a download URL for a symbol file (SVG). The URL includes a short-lived JWT (10 min). Use curl to download.""" - return await asyncio.to_thread(_run_handler_sync, get_symbol_handler, { - "symbol_id": symbol_id, - }) - - -@mcp.tool() -async def symbol_dimensions( - symbol_id: Annotated[str, Field(description="Symbol ID to get dimensions for")], -) -> list[dict[str, Any]]: - """Get the dimensions (width, height) of a symbol.""" - return await asyncio.to_thread(_run_handler_sync, get_symbol_dimensions_handler, { - "symbol_id": symbol_id, - }) - - -@mcp.tool() -async def symbol_defaults() -> list[dict[str, Any]]: - """Get the default symbol mapping for each node type.""" - return await asyncio.to_thread(_run_handler_sync, get_default_symbols_handler, {}) - - -@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. Provide the SVG content as a string.""" - return await asyncio.to_thread(_run_handler_sync, upload_symbol_handler, { - "symbol_id": symbol_id, "content": content, - }) - - -@mcp.tool() -async def symbol_delete( - symbol_id: Annotated[str, Field(description="Symbol ID to delete (e.g. ':/symbols/my_custom_symbol.svg'). Use symbol_list to get existing IDs.")], -) -> list[dict[str, Any]]: - """Delete a custom symbol from the server. - - NOTE: Only custom (user-uploaded) symbols can be deleted. - Built-in symbols (starting with ':/symbols/') will be rejected with 403. - Use symbol_list to see which symbols are available and their IDs. - """ - return await asyncio.to_thread(_run_handler_sync, delete_symbol_handler, { - "symbol_id": symbol_id, - }) +# +# Disabled for now: symbol handling requires a vision-capable model (the +# tools shuttle SVG content, which a text-only LLM cannot inspect or +# produce). Revisit later. +# +# @mcp.tool() +# async def symbol_list() -> list[dict[str, Any]]: +# """List all available symbols on the server.""" +# return await asyncio.to_thread(_run_handler_sync, get_symbols_handler, {}) +# +# +# @mcp.tool() +# async def symbol_get( +# symbol_id: Annotated[str, Field(description="Symbol ID (e.g. ':/symbols/router.svg')")], +# ) -> list[dict[str, Any]]: +# """Get a download URL for a symbol file (SVG). The URL includes a short-lived JWT (10 min). Use curl to download.""" +# return await asyncio.to_thread(_run_handler_sync, get_symbol_handler, { +# "symbol_id": symbol_id, +# }) +# +# +# @mcp.tool() +# async def symbol_dimensions( +# symbol_id: Annotated[str, Field(description="Symbol ID to get dimensions for")], +# ) -> list[dict[str, Any]]: +# """Get the dimensions (width, height) of a symbol.""" +# return await asyncio.to_thread(_run_handler_sync, get_symbol_dimensions_handler, { +# "symbol_id": symbol_id, +# }) +# +# +# @mcp.tool() +# async def symbol_defaults() -> list[dict[str, Any]]: +# """Get the default symbol mapping for each node type.""" +# return await asyncio.to_thread(_run_handler_sync, get_default_symbols_handler, {}) +# +# +# @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. Provide the SVG content as a string.""" +# return await asyncio.to_thread(_run_handler_sync, upload_symbol_handler, { +# "symbol_id": symbol_id, "content": content, +# }) +# +# +# @mcp.tool() +# async def symbol_delete( +# symbol_id: Annotated[str, Field(description="Symbol ID to delete (e.g. ':/symbols/my_custom_symbol.svg'). Use symbol_list to get existing IDs.")], +# ) -> list[dict[str, Any]]: +# """Delete a custom symbol from the server. +# +# NOTE: Only custom (user-uploaded) symbols can be deleted. +# Built-in symbols (starting with ':/symbols/') will be rejected with 403. +# Use symbol_list to see which symbols are available and their IDs. +# """ +# return await asyncio.to_thread(_run_handler_sync, delete_symbol_handler, { +# "symbol_id": symbol_id, +# }) # ── Appliance tools ───────────────────────────────────────────────────