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
This commit is contained in:
YueGuobin 2026-06-26 00:17:02 +08:00
parent 58d3055dd3
commit 292b60efaa
No known key found for this signature in database
11 changed files with 117 additions and 58 deletions

View File

@ -58,11 +58,11 @@ python3 -m pip install gns3-server
GNS3 server supports optional features that can be installed as needed: GNS3 server supports optional features that can be installed as needed:
**AI Copilot** (Optional): **AI Features** (Optional — includes AI Copilot and MCP):
```shell ```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): **Development** (For contributors):
```shell ```shell
@ -78,7 +78,7 @@ Browser-based packet capture analysis using Wireshark in a Docker container.
**Combination Installation**: **Combination Installation**:
You can install multiple optional features together: You can install multiple optional features together:
```shell ```shell
python3 -m pip install gns3-server[ai-copilot,dev] python3 -m pip install gns3-server[ai-features,dev]
``` ```
**Why optional?** **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. **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 ```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. 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.
The downside of this method is you will have to manually install all dependencies (see below).
Please see our [documentation](https://docs.gns3.com/docs/getting-started/installation/linux) for more details. 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: **For AI Copilot development**, install with additional dependencies:
```shell ```shell
python3 -m pip install .[ai-copilot,dev] python3 -m pip install .[ai-features,dev]
``` ```
**For development (tests and linting)**: **For development (tests and linting)**:

View File

@ -1,7 +1,7 @@
# ============================================================================== # ==============================================================================
# GNS3 Copilot AI Agent Dependencies # 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 # Or directly: pip install -r ai-requirements.txt
# ============================================================================== # ==============================================================================

View File

@ -104,7 +104,7 @@ source venv/bin/activate
# pip install -e . -i https://mirrors.aliyun.com/pypi/simple/ # pip install -e . -i https://mirrors.aliyun.com/pypi/simple/
pip install -e . && gns3server-web-wireshark-setup pip install -e . && gns3server-web-wireshark-setup
pip install -e .[ai-copilot] pip install -e .[ai-features]
pip install -e .[dev] pip install -e .[dev]
``` ```
@ -114,10 +114,10 @@ Run the server:
python3 -m gns3server python3 -m gns3server
``` ```
## Optional: Install AI Copilot Development Dependencies ## Optional: Install AI Features Development Dependencies
```bash ```bash
python3 -m pip install .[ai-copilot,dev] python3 -m pip install .[ai-features,dev]
``` ```
## Optional: Expand LVM Root Partition ## Optional: Expand LVM Root Partition

View File

@ -15,14 +15,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
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. This module provides the AI Copilot and MCP (Model Context Protocol)
If the AI dependencies are not installed, the module will be disabled but functionality as optional features. If the respective dependencies are
will not prevent the server from starting. not installed, the affected features will be disabled but will not
prevent the server from starting.
Installation: Installation:
pip install gns3-server[ai-copilot] pip install gns3-server[ai-features] # Install all AI features
""" """
import logging import logging
@ -49,7 +50,8 @@ except ImportError as e:
# AI dependencies not installed, disable AI Copilot feature # AI dependencies not installed, disable AI Copilot feature
logging.warning( logging.warning(
f"AI Copilot dependencies not installed: {e}. " 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 AI_COPILOT_AVAILABLE = False
@ -63,7 +65,7 @@ except ImportError as e:
""" """
raise RuntimeError( raise RuntimeError(
"AI Copilot is not available. " "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: class ProjectAgentManager:
@ -74,12 +76,30 @@ except ImportError as e:
def __init__(self): def __init__(self):
raise RuntimeError( raise RuntimeError(
"AI Copilot is not available. " "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__ = [ __all__ = [
"AI_COPILOT_AVAILABLE", "AI_COPILOT_AVAILABLE",
"MCP_AVAILABLE",
"get_project_agent_manager", "get_project_agent_manager",
"ProjectAgentManager", "ProjectAgentManager",
] ]

View File

@ -39,7 +39,7 @@ else:
async def ai_not_available(path: str = ""): async def ai_not_available(path: str = ""):
raise HTTPException( raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, 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 from . import controller

View File

@ -45,9 +45,26 @@ from gns3server.controller.controller_error import (
from gns3server.api.routes import controller, index from gns3server.api.routes import controller, index
from gns3server.api.routes.compute import compute_api from gns3server.api.routes.compute import compute_api
from gns3server.api.routes import mcp
from gns3server.core import tasks 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 import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -76,7 +93,9 @@ def get_application() -> FastAPI:
application.include_router(controller.router, prefix="/v3") application.include_router(controller.router, prefix="/v3")
application.mount("/static", StaticFiles(packages=[('gns3server', 'static')], html=True), name="static") application.mount("/static", StaticFiles(packages=[('gns3server', 'static')], html=True), name="static")
application.mount("/v3/compute", compute_api, name="compute") 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 return application
@ -84,7 +103,8 @@ def get_application() -> FastAPI:
app = get_application() app = get_application()
# Register MCP SSE transport routes (Starlette-level, for raw ASGI access) # 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 # Monkey Patch uvicorn signal handler to detect the application is shutting down
app.state.exiting = False app.state.exiting = False

View File

@ -84,9 +84,12 @@ async def startup(app: FastAPI) -> None:
m = module.instance() m = module.instance()
m.port_manager = PortManager.instance() m.port_manager = PortManager.instance()
# Mark MCP server as ready to accept connections # Mark MCP server as ready to accept connections (if MCP is available)
from gns3server.api.routes.mcp import set_mcp_server_ready from gns3server.agent import MCP_AVAILABLE
set_mcp_server_ready(True)
if MCP_AVAILABLE:
from gns3server.api.routes.mcp import set_mcp_server_ready
set_mcp_server_ready(True)
log.info("GNS3 server startup completed") log.info("GNS3 server startup completed")

View File

@ -1,10 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Uninstall AI Copilot dependencies. Uninstall AI Features dependencies (AI Copilot + MCP).
Usage: Usage:
gns3server-uninstall-ai-copilot gns3server-uninstall-ai-features
gns3server-uninstall-ai-copilot -y gns3server-uninstall-ai-features -y
""" """
import os import os
@ -13,38 +13,50 @@ import subprocess
import argparse import argparse
def get_ai_packages(): def _find_base_dir():
"""Read packages from ai-requirements.txt.""" """Find the project root directory."""
# Find the requirements file
if hasattr(sys, '_MEIPASS'): if hasattr(sys, '_MEIPASS'):
# PyInstaller bundle return os.path.dirname(sys.executable)
base_dir = os.path.dirname(sys.executable) return os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
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__))))
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 = [] packages = []
with open(requirements_file, "r") as f: with open(filepath, "r") as f:
for line in f: for line in f:
line = line.strip() line = line.strip()
if not line or line.startswith("#"): if not line or line.startswith("#"):
continue continue
# Remove version specifiers
package = line.split(">=")[0].split("==")[0].split("~=")[0] package = line.split(">=")[0].split("==")[0].split("~=")[0]
packages.append(package) packages.append(package)
return packages 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): def uninstall(packages, yes=False):
"""Uninstall packages.""" """Uninstall packages."""
if not packages: if not packages:
print("No packages found to uninstall.") print("No packages found to uninstall.")
return return
print(f"Found {len(packages)} AI Copilot dependencies:") print(f"Found {len(packages)} AI Features dependencies:")
for pkg in packages: for pkg in packages:
print(f" - {pkg}") print(f" - {pkg}")
print() print()
@ -70,13 +82,13 @@ def uninstall(packages, yes=False):
except Exception as e: except Exception as e:
print(f" Error removing {package}: {e}") print(f" Error removing {package}: {e}")
print("\nAI Copilot dependencies have been uninstalled.") print("\nAI Features dependencies have been uninstalled.")
print("You can reinstall them with: pip install gns3-server[ai-copilot]") print("You can reinstall them with: pip install gns3-server[ai-features]")
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Uninstall AI Copilot dependencies" description="Uninstall AI Features dependencies (AI Copilot + MCP)"
) )
parser.add_argument( parser.add_argument(
"-y", "--yes", action="store_true", "-y", "--yes", action="store_true",

8
mcp-requirements.txt Normal file
View File

@ -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

View File

@ -38,8 +38,8 @@ dependencies = {file = "requirements.txt"}
# Development dependencies # Development dependencies
dev = {file = ['dev-requirements.txt']} dev = {file = ['dev-requirements.txt']}
# Optional components (can be installed individually or combined) # AI features bundle — installs both AI Copilot and MCP dependencies
ai-copilot = {file = ['ai-requirements.txt']} ai-features = {file = ['ai-requirements.txt', 'mcp-requirements.txt']}
# terraform = {file = ['terraform-requirements.txt']} # Future component # terraform = {file = ['terraform-requirements.txt']} # Future component
[project.urls] [project.urls]
@ -50,8 +50,8 @@ ai-copilot = {file = ['ai-requirements.txt']}
[project.scripts] [project.scripts]
gns3server = "gns3server.main:main" gns3server = "gns3server.main:main"
gns3vmnet = "gns3server.utils.vmnet: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-web-wireshark-setup = "gns3server.agent.web_wireshark.setup_wireshark_image:main"
gns3server-uninstall-ai-features = "gns3server.utils.uninstall_ai_features:main"
[tool.setuptools] [tool.setuptools]
packages = ["gns3server"] packages = ["gns3server"]

View File

@ -31,19 +31,17 @@ typing-extensions>=4.15.0
requests>=2.34.2 requests>=2.34.2
urllib3>=2.7.0 urllib3>=2.7.0
# MCP (Model Context Protocol) dependencies
fastmcp>=3.4.0
# ============================================================================== # ==============================================================================
# File type detection # File type detection
python-magic>=0.4.27 python-magic>=0.4.27
# AI Copilot Optional Dependencies # Optional AI Features (AI Copilot + MCP)
# ============================================================================== # ==============================================================================
# AI Copilot features are now optional. Install with: # AI Copilot and MCP features are now optional. Install with:
# pip install gns3-server[ai-copilot] # pip install gns3-server[ai-features]
# Or: # Or:
# pip install -r ai-requirements.txt # pip install -r ai-requirements.txt
# pip install -r mcp-requirements.txt
# #
# This reduces installation size and supports restricted environments where # This reduces installation size and supports restricted environments where
# AI dependencies may not be allowed. # AI dependencies may not be allowed.