From 292b60efaad1fccc3c8e741ea0cd6c38e692e3fa Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Fri, 26 Jun 2026 00:17:02 +0800 Subject: [PATCH 1/2] Make AI features (AI Copilot + MCP) optional via [ai-features] extra - Move fastmcp from core requirements.txt to mcp-requirements.txt - Add MCP_AVAILABLE feature flag in agent/__init__.py (graceful degradation) - Guard MCP imports/registration in server.py and tasks.py - Replace ai-copilot/mcp/ai-support extras with single ai-features extra - Add stub MCP routes returning 501 when MCP is not installed - Add gns3server-uninstall-ai-features CLI command - Remove old gns3server-uninstall-ai-copilot command - Update all error messages and docs to reference ai-features Closes #2794 --- README.md | 20 ++++---- ai-requirements.txt | 2 +- docs/development-setup.md | 6 +-- gns3server/agent/__init__.py | 36 ++++++++++--- gns3server/api/routes/controller/__init__.py | 2 +- gns3server/api/server.py | 26 ++++++++-- gns3server/core/tasks.py | 9 ++-- ...ai_copilot.py => uninstall_ai_features.py} | 50 ++++++++++++------- mcp-requirements.txt | 8 +++ pyproject.toml | 6 +-- requirements.txt | 10 ++-- 11 files changed, 117 insertions(+), 58 deletions(-) rename gns3server/utils/{uninstall_ai_copilot.py => uninstall_ai_features.py} (60%) create mode 100644 mcp-requirements.txt diff --git a/README.md b/README.md index e21dbd0ab..4144beafd 100644 --- a/README.md +++ b/README.md @@ -58,11 +58,11 @@ python3 -m pip install gns3-server GNS3 server supports optional features that can be installed as needed: -**AI Copilot** (Optional): +**AI Features** (Optional — includes AI Copilot and MCP): ```shell -python3 -m pip install gns3-server[ai-copilot] +python3 -m pip install gns3-server[ai-features] ``` -AI-powered assistant for network topology design and automation. +AI-powered assistant for network topology design, automation, and MCP protocol support for AI agent integration. **Development** (For contributors): ```shell @@ -78,7 +78,7 @@ Browser-based packet capture analysis using Wireshark in a Docker container. **Combination Installation**: You can install multiple optional features together: ```shell -python3 -m pip install gns3-server[ai-copilot,dev] +python3 -m pip install gns3-server[ai-features,dev] ``` **Why optional?** @@ -89,17 +89,15 @@ python3 -m pip install gns3-server[ai-copilot,dev] **Note:** If you install without optional extras, the server will work normally but optional features will be disabled. You can add features later by running the appropriate install command. -**Uninstalling AI Copilot:** +**Uninstalling AI Features:** -To remove AI Copilot dependencies: +To remove AI Features dependencies (AI Copilot + MCP): ```shell -gns3server-uninstall-ai-copilot +gns3server-uninstall-ai-features ``` -This will remove all AI Copilot dependencies while keeping the core functionality intact. The server will continue to work, but AI features will return a 501 (Not Implemented) status code. - -The downside of this method is you will have to manually install all dependencies (see below). +This will remove all AI Copilot and MCP dependencies while keeping the core functionality intact. The server will continue to work, but AI features will be disabled. Please see our [documentation](https://docs.gns3.com/docs/getting-started/installation/linux) for more details. @@ -137,7 +135,7 @@ python3 -m gns3server **For AI Copilot development**, install with additional dependencies: ```shell -python3 -m pip install .[ai-copilot,dev] +python3 -m pip install .[ai-features,dev] ``` **For development (tests and linting)**: diff --git a/ai-requirements.txt b/ai-requirements.txt index caf1e0965..135af4c09 100644 --- a/ai-requirements.txt +++ b/ai-requirements.txt @@ -1,7 +1,7 @@ # ============================================================================== # GNS3 Copilot AI Agent Dependencies # ============================================================================== -# Install with: pip install gns3-server[ai-copilot] +# Install with: pip install gns3-server[ai-features] # Or directly: pip install -r ai-requirements.txt # ============================================================================== diff --git a/docs/development-setup.md b/docs/development-setup.md index 3403972eb..2ee546560 100644 --- a/docs/development-setup.md +++ b/docs/development-setup.md @@ -104,7 +104,7 @@ source venv/bin/activate # pip install -e . -i https://mirrors.aliyun.com/pypi/simple/ pip install -e . && gns3server-web-wireshark-setup -pip install -e .[ai-copilot] +pip install -e .[ai-features] pip install -e .[dev] ``` @@ -114,10 +114,10 @@ Run the server: python3 -m gns3server ``` -## Optional: Install AI Copilot Development Dependencies +## Optional: Install AI Features Development Dependencies ```bash -python3 -m pip install .[ai-copilot,dev] +python3 -m pip install .[ai-features,dev] ``` ## Optional: Expand LVM Root Partition diff --git a/gns3server/agent/__init__.py b/gns3server/agent/__init__.py index 1861ed37e..e6a0f8491 100644 --- a/gns3server/agent/__init__.py +++ b/gns3server/agent/__init__.py @@ -15,14 +15,15 @@ # along with this program. If not, see . """ -Agent module with optional AI Copilot support. +Agent module with optional AI Copilot and MCP support. -This module provides the AI Copilot functionality as an optional feature. -If the AI dependencies are not installed, the module will be disabled but -will not prevent the server from starting. +This module provides the AI Copilot and MCP (Model Context Protocol) +functionality as optional features. If the respective dependencies are +not installed, the affected features will be disabled but will not +prevent the server from starting. Installation: - pip install gns3-server[ai-copilot] + pip install gns3-server[ai-features] # Install all AI features """ import logging @@ -49,7 +50,8 @@ except ImportError as e: # AI dependencies not installed, disable AI Copilot feature logging.warning( f"AI Copilot dependencies not installed: {e}. " - "AI features will be disabled. Install with: pip install gns3-server[ai-copilot]" + "AI features will be disabled. " + "Install with: pip install gns3-server[ai-features]" ) AI_COPILOT_AVAILABLE = False @@ -63,7 +65,7 @@ except ImportError as e: """ raise RuntimeError( "AI Copilot is not available. " - "Install AI dependencies with: pip install gns3-server[ai-copilot]" + "Install AI dependencies with: pip install gns3-server[ai-features]" ) class ProjectAgentManager: @@ -74,12 +76,30 @@ except ImportError as e: def __init__(self): raise RuntimeError( "AI Copilot is not available. " - "Install AI dependencies with: pip install gns3-server[ai-copilot]" + "Install AI dependencies with: pip install gns3-server[ai-features]" ) +# Feature flag: MCP (Model Context Protocol) is available +MCP_AVAILABLE = False + +# Try to import MCP dependencies +try: + import mcp.server.fastmcp # noqa: F401 — test import only + MCP_AVAILABLE = True +except ImportError: + # MCP dependencies not installed, disable MCP feature + logging.warning( + "MCP dependencies not installed. " + "MCP features will be disabled. " + "Install with: pip install gns3-server[ai-features]" + ) + MCP_AVAILABLE = False + + __all__ = [ "AI_COPILOT_AVAILABLE", + "MCP_AVAILABLE", "get_project_agent_manager", "ProjectAgentManager", ] diff --git a/gns3server/api/routes/controller/__init__.py b/gns3server/api/routes/controller/__init__.py index 2891642f9..5facc2499 100644 --- a/gns3server/api/routes/controller/__init__.py +++ b/gns3server/api/routes/controller/__init__.py @@ -39,7 +39,7 @@ else: async def ai_not_available(path: str = ""): raise HTTPException( status_code=status.HTTP_501_NOT_IMPLEMENTED, - detail="AI Copilot is not available. Install AI dependencies with: pip install gns3-server[ai-copilot]" + detail="AI Copilot is not available. Install AI dependencies with: pip install gns3-server[ai-features]" ) from . import controller diff --git a/gns3server/api/server.py b/gns3server/api/server.py index e6089e9fb..064b3d810 100644 --- a/gns3server/api/server.py +++ b/gns3server/api/server.py @@ -45,9 +45,26 @@ from gns3server.controller.controller_error import ( from gns3server.api.routes import controller, index from gns3server.api.routes.compute import compute_api -from gns3server.api.routes import mcp from gns3server.core import tasks +# MCP is an optional feature — import only if dependencies are installed +from gns3server.agent import MCP_AVAILABLE + +if MCP_AVAILABLE: + from gns3server.api.routes import mcp + _mcp_router = mcp.router +else: + from fastapi import APIRouter + + _mcp_router = APIRouter(prefix="/mcp", tags=["MCP"]) + + @_mcp_router.api_route("/{path:path}", methods=["GET", "POST", "DELETE", "PATCH", "PUT"]) + async def mcp_not_available(path: str = ""): + raise HTTPException( + status_code=status.HTTP_501_NOT_IMPLEMENTED, + detail="MCP is not available. Install AI dependencies with: pip install gns3-server[ai-features]" + ) + import logging log = logging.getLogger(__name__) @@ -76,7 +93,9 @@ def get_application() -> FastAPI: application.include_router(controller.router, prefix="/v3") application.mount("/static", StaticFiles(packages=[('gns3server', 'static')], html=True), name="static") application.mount("/v3/compute", compute_api, name="compute") - application.include_router(mcp.router, prefix="/v3", tags=["MCP"]) + + # Register MCP routes (stub returns 501 if MCP dependencies are not installed) + application.include_router(_mcp_router, prefix="/v3", tags=["MCP"]) return application @@ -84,7 +103,8 @@ def get_application() -> FastAPI: app = get_application() # Register MCP SSE transport routes (Starlette-level, for raw ASGI access) -mcp.register_starlette_routes(app) +if MCP_AVAILABLE: + mcp.register_starlette_routes(app) # Monkey Patch uvicorn signal handler to detect the application is shutting down app.state.exiting = False diff --git a/gns3server/core/tasks.py b/gns3server/core/tasks.py index 08afc2198..d4968d8f5 100644 --- a/gns3server/core/tasks.py +++ b/gns3server/core/tasks.py @@ -84,9 +84,12 @@ async def startup(app: FastAPI) -> None: m = module.instance() m.port_manager = PortManager.instance() - # Mark MCP server as ready to accept connections - from gns3server.api.routes.mcp import set_mcp_server_ready - set_mcp_server_ready(True) + # Mark MCP server as ready to accept connections (if MCP is available) + from gns3server.agent import MCP_AVAILABLE + + if MCP_AVAILABLE: + from gns3server.api.routes.mcp import set_mcp_server_ready + set_mcp_server_ready(True) log.info("GNS3 server startup completed") diff --git a/gns3server/utils/uninstall_ai_copilot.py b/gns3server/utils/uninstall_ai_features.py similarity index 60% rename from gns3server/utils/uninstall_ai_copilot.py rename to gns3server/utils/uninstall_ai_features.py index c4908af83..44b545e25 100644 --- a/gns3server/utils/uninstall_ai_copilot.py +++ b/gns3server/utils/uninstall_ai_features.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 """ -Uninstall AI Copilot dependencies. +Uninstall AI Features dependencies (AI Copilot + MCP). Usage: - gns3server-uninstall-ai-copilot - gns3server-uninstall-ai-copilot -y + gns3server-uninstall-ai-features + gns3server-uninstall-ai-features -y """ import os @@ -13,38 +13,50 @@ import subprocess import argparse -def get_ai_packages(): - """Read packages from ai-requirements.txt.""" - # Find the requirements file +def _find_base_dir(): + """Find the project root directory.""" if hasattr(sys, '_MEIPASS'): - # PyInstaller bundle - base_dir = os.path.dirname(sys.executable) - else: - # __file__ = gns3server/utils/uninstall_ai_copilot.py - # Need to go up 2 levels to reach project root - base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + return os.path.dirname(sys.executable) + return os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - requirements_file = os.path.join(base_dir, "ai-requirements.txt") +def _read_requirements(filename): + """Read packages from a requirements file.""" + base_dir = _find_base_dir() + filepath = os.path.join(base_dir, filename) packages = [] - with open(requirements_file, "r") as f: + with open(filepath, "r") as f: for line in f: line = line.strip() if not line or line.startswith("#"): continue - # Remove version specifiers package = line.split(">=")[0].split("==")[0].split("~=")[0] packages.append(package) return packages +def get_ai_packages(): + """Read packages from all AI features requirements files.""" + packages = [] + packages.extend(_read_requirements("ai-requirements.txt")) + packages.extend(_read_requirements("mcp-requirements.txt")) + # Remove duplicates while preserving order + seen = set() + unique = [] + for pkg in packages: + if pkg not in seen: + seen.add(pkg) + unique.append(pkg) + return unique + + def uninstall(packages, yes=False): """Uninstall packages.""" if not packages: print("No packages found to uninstall.") return - print(f"Found {len(packages)} AI Copilot dependencies:") + print(f"Found {len(packages)} AI Features dependencies:") for pkg in packages: print(f" - {pkg}") print() @@ -70,13 +82,13 @@ def uninstall(packages, yes=False): except Exception as e: print(f" Error removing {package}: {e}") - print("\nAI Copilot dependencies have been uninstalled.") - print("You can reinstall them with: pip install gns3-server[ai-copilot]") + print("\nAI Features dependencies have been uninstalled.") + print("You can reinstall them with: pip install gns3-server[ai-features]") def main(): parser = argparse.ArgumentParser( - description="Uninstall AI Copilot dependencies" + description="Uninstall AI Features dependencies (AI Copilot + MCP)" ) parser.add_argument( "-y", "--yes", action="store_true", diff --git a/mcp-requirements.txt b/mcp-requirements.txt new file mode 100644 index 000000000..11f4e4fc8 --- /dev/null +++ b/mcp-requirements.txt @@ -0,0 +1,8 @@ +# ============================================================================== +# GNS3 MCP (Model Context Protocol) Service Dependencies +# ============================================================================== +# Install with: pip install gns3-server[ai-features] +# Or directly: pip install -r mcp-requirements.txt +# ============================================================================== + +fastmcp>=3.4.0 diff --git a/pyproject.toml b/pyproject.toml index 5d9a175dc..e47c297ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,8 +38,8 @@ dependencies = {file = "requirements.txt"} # Development dependencies dev = {file = ['dev-requirements.txt']} -# Optional components (can be installed individually or combined) -ai-copilot = {file = ['ai-requirements.txt']} +# AI features bundle — installs both AI Copilot and MCP dependencies +ai-features = {file = ['ai-requirements.txt', 'mcp-requirements.txt']} # terraform = {file = ['terraform-requirements.txt']} # Future component [project.urls] @@ -50,8 +50,8 @@ ai-copilot = {file = ['ai-requirements.txt']} [project.scripts] gns3server = "gns3server.main:main" gns3vmnet = "gns3server.utils.vmnet:main" -gns3server-uninstall-ai-copilot = "gns3server.utils.uninstall_ai_copilot:main" gns3server-web-wireshark-setup = "gns3server.agent.web_wireshark.setup_wireshark_image:main" +gns3server-uninstall-ai-features = "gns3server.utils.uninstall_ai_features:main" [tool.setuptools] packages = ["gns3server"] diff --git a/requirements.txt b/requirements.txt index 225300c42..9986b177e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,19 +31,17 @@ typing-extensions>=4.15.0 requests>=2.34.2 urllib3>=2.7.0 -# MCP (Model Context Protocol) dependencies -fastmcp>=3.4.0 - # ============================================================================== # File type detection python-magic>=0.4.27 -# AI Copilot Optional Dependencies +# Optional AI Features (AI Copilot + MCP) # ============================================================================== -# AI Copilot features are now optional. Install with: -# pip install gns3-server[ai-copilot] +# AI Copilot and MCP features are now optional. Install with: +# pip install gns3-server[ai-features] # Or: # pip install -r ai-requirements.txt +# pip install -r mcp-requirements.txt # # This reduces installation size and supports restricted environments where # AI dependencies may not be allowed. From e0ef63ebc1b46680984df41a6070e3e7d598a50b Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sat, 27 Jun 2026 22:19:39 +0800 Subject: [PATCH 2/2] Update CI to install [ai-features,dev] instead of [ai-copilot,dev] The [ai-copilot] extra has been replaced by [ai-features] which includes both AI Copilot and MCP dependencies. --- .github/workflows/testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index f896a3b90..fe94b2aa0 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -31,7 +31,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install .[ai-copilot,dev] + python -m pip install .[ai-features,dev] - name: Install Windows specific dependencies if: runner.os == 'Windows'