Skip to main content
Glama

GNS3 Network Simulator MCP Server

by Wael-Rd
server.pyโ€ข29 kB
#!/usr/bin/env python3 """ GNS3 MCP Server - FastMCP implementation for GNS3 network simulation integration. This MCP server provides tools for managing GNS3 network topologies, project management, and simulation control through direct HTTP API calls. """ import asyncio import json import logging from typing import Any, Dict, List, Optional import httpx from fastmcp import FastMCP from pydantic import BaseModel, Field # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Create FastMCP server instance mcp = FastMCP("GNS3 Network Simulator") # Configuration models class GNS3Config(BaseModel): """Configuration for GNS3 server connection.""" server_url: str = Field(default="http://localhost:3080", description="GNS3 server URL") username: Optional[str] = Field(default=None, description="Username for authentication") password: Optional[str] = Field(default=None, description="Password for authentication") verify_ssl: bool = Field(default=True, description="Verify SSL certificates") class GNS3APIClient: """HTTP client for GNS3 REST API.""" def __init__(self, config: GNS3Config): self.config = config self.base_url = config.server_url.rstrip('/') self.auth = None if config.username and config.password: self.auth = (config.username, config.password) async def _request(self, method: str, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]: """Make HTTP request to GNS3 API.""" url = f"{self.base_url}/v3{endpoint}" headers = {"Content-Type": "application/json"} try: async with httpx.AsyncClient(verify=self.config.verify_ssl, timeout=30.0) as client: if method.upper() == "GET": response = await client.get(url, headers=headers, auth=self.auth) elif method.upper() == "POST": response = await client.post(url, json=data, headers=headers, auth=self.auth) elif method.upper() == "PUT": response = await client.put(url, json=data, headers=headers, auth=self.auth) elif method.upper() == "DELETE": response = await client.delete(url, headers=headers, auth=self.auth) else: raise ValueError(f"Unsupported HTTP method: {method}") response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"Request error: {e}") raise Exception(f"Failed to connect to GNS3 server: {e}") except httpx.HTTPStatusError as e: logger.error(f"HTTP error: {e}") raise Exception(f"GNS3 API error: {e.response.status_code} - {e.response.text}") async def get_server_info(self) -> Dict[str, Any]: """Get GNS3 server information.""" return await self._request("GET", "/version") async def get_projects(self) -> List[Dict[str, Any]]: """Get all projects.""" return await self._request("GET", "/projects") async def create_project(self, name: str, auto_close: bool = False) -> Dict[str, Any]: """Create a new project.""" data = { "name": name, "auto_close": auto_close } return await self._request("POST", "/projects", data) async def get_project(self, project_id: str) -> Dict[str, Any]: """Get project details.""" return await self._request("GET", f"/projects/{project_id}") async def open_project(self, project_id: str) -> Dict[str, Any]: """Open a project.""" return await self._request("PUT", f"/projects/{project_id}/open") async def get_project_nodes(self, project_id: str) -> List[Dict[str, Any]]: """Get all nodes in a project.""" return await self._request("GET", f"/projects/{project_id}/nodes") async def get_project_links(self, project_id: str) -> List[Dict[str, Any]]: """Get all links in a project.""" return await self._request("GET", f"/projects/{project_id}/links") async def create_node(self, project_id: str, node_data: Dict[str, Any]) -> Dict[str, Any]: """Create a new node.""" return await self._request("POST", f"/projects/{project_id}/nodes", node_data) async def get_node(self, project_id: str, node_id: str) -> Dict[str, Any]: """Get node details.""" return await self._request("GET", f"/projects/{project_id}/nodes/{node_id}") async def update_node(self, project_id: str, node_id: str, node_data: Dict[str, Any]) -> Dict[str, Any]: """Update node.""" return await self._request("PUT", f"/projects/{project_id}/nodes/{node_id}", node_data) async def start_node(self, project_id: str, node_id: str) -> Dict[str, Any]: """Start a node.""" return await self._request("POST", f"/projects/{project_id}/nodes/{node_id}/start") async def stop_node(self, project_id: str, node_id: str) -> Dict[str, Any]: """Stop a node.""" return await self._request("POST", f"/projects/{project_id}/nodes/{node_id}/stop") async def create_link(self, project_id: str, link_data: Dict[str, Any]) -> Dict[str, Any]: """Create a new link.""" return await self._request("POST", f"/projects/{project_id}/links", link_data) async def start_capture(self, project_id: str, link_id: str, capture_file_name: str) -> Dict[str, Any]: """Start packet capture on a link.""" data = {"capture_file_name": capture_file_name} return await self._request("POST", f"/projects/{project_id}/links/{link_id}/start_capture", data) async def create_snapshot(self, project_id: str, name: str) -> Dict[str, Any]: """Create a project snapshot.""" data = {"name": name} return await self._request("POST", f"/projects/{project_id}/snapshots", data) # Tool: List all GNS3 projects @mcp.tool async def gns3_list_projects( server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None ) -> Dict[str, Any]: """List all projects on the GNS3 server. Args: server_url: GNS3 server URL (default: http://localhost:3080) username: Optional username for authentication password: Optional password for authentication Returns: Dictionary containing project list with status """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Get server info server_info = await client.get_server_info() # Get projects projects = await client.get_projects() # Format projects summary projects_summary = [] for project in projects: projects_summary.append({ "Project Name": project.get("name", "Unnamed"), "Project ID": project.get("project_id", ""), "Total Nodes": project.get("stats", {}).get("nodes", 0), "Total Links": project.get("stats", {}).get("links", 0), "Status": project.get("status", "unknown") }) return { "status": "success", "server_info": { "version": server_info.get("version", "unknown"), "user": server_info.get("user", "anonymous") }, "projects": projects_summary, "total_projects": len(projects_summary) } except Exception as e: logger.error(f"Failed to list projects: {e}") return { "status": "error", "error": str(e), "projects": [], "total_projects": 0 } # Tool: Create a new GNS3 project @mcp.tool async def gns3_create_project( name: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None, auto_close: bool = False ) -> Dict[str, Any]: """Create a new GNS3 project. Args: name: Name for the new project server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication auto_close: Auto-close the project when done Returns: Dictionary containing project creation result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Create project project = await client.create_project(name, auto_close) return { "status": "success", "project": { "project_id": project.get("project_id"), "name": project.get("name"), "status": project.get("status"), "auto_close": project.get("auto_close", False) } } except Exception as e: logger.error(f"Failed to create project: {e}") return { "status": "error", "error": str(e), "project": None } # Tool: Open an existing project @mcp.tool async def gns3_open_project( project_id: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None ) -> Dict[str, Any]: """Open an existing GNS3 project. Args: project_id: ID of the project to open server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication Returns: Dictionary containing project opening result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Get project details project = await client.get_project(project_id) # Open project opened_project = await client.open_project(project_id) return { "status": "success", "project": { "project_id": opened_project.get("project_id"), "name": opened_project.get("name"), "status": opened_project.get("status"), "auto_close": opened_project.get("auto_close", False), "stats": opened_project.get("stats", {}) } } except Exception as e: logger.error(f"Failed to open project: {e}") return { "status": "error", "error": str(e), "project": None } # Tool: Add a network node/device @mcp.tool async def gns3_add_node( project_id: str, node_name: str, node_type: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None, x: Optional[int] = None, y: Optional[int] = None, console_type: Optional[str] = None, console_auto_start: bool = False ) -> Dict[str, Any]: """Add a network device/node to a GNS3 project. Args: project_id: ID of the project to add node to node_name: Name for the new node node_type: Type of node (e.g., 'router', 'ethernet_switch', 'vpcs') server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication x: X coordinate on the canvas (optional) y: Y coordinate on the canvas (optional) console_type: Console type (optional) console_auto_start: Auto-start console (optional) Returns: Dictionary containing node addition result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Prepare node data node_data = { "name": node_name, "node_type": node_type } if x is not None: node_data["x"] = x if y is not None: node_data["y"] = y if console_type is not None: node_data["console_type"] = console_type if console_auto_start: node_data["console_auto_start"] = console_auto_start # Create node node = await client.create_node(project_id, node_data) return { "status": "success", "node": { "node_id": node.get("node_id"), "name": node.get("name"), "node_type": node.get("node_type"), "status": node.get("status"), "x": node.get("x"), "y": node.get("y"), "console": node.get("console") } } except Exception as e: logger.error(f"Failed to add node: {e}") return { "status": "error", "error": str(e), "node": None } # Tool: Add a link between two nodes @mcp.tool async def gns3_add_link( project_id: str, node_a_id: str, node_b_id: str, node_a_port: Optional[str] = None, node_b_port: Optional[str] = None, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None ) -> Dict[str, Any]: """Add a link between two nodes in a GNS3 project. Args: project_id: ID of the project node_a_id: ID of the first node node_b_id: ID of the second node node_a_port: Port on the first node (optional) node_b_port: Port on the second node (optional) server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication Returns: Dictionary containing link addition result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Prepare link data link_data = { "nodes": [ { "node_id": node_a_id, "port_name": node_a_port }, { "node_id": node_b_id, "port_name": node_b_port } ] } # Create link link = await client.create_link(project_id, link_data) return { "status": "success", "link": { "link_id": link.get("link_id"), "link_type": link.get("link_type", "ethernet"), "nodes": link.get("nodes", []), "capturing": link.get("capturing", False) } } except Exception as e: logger.error(f"Failed to add link: {e}") return { "status": "error", "error": str(e), "link": None } # Tool: Configure device settings @mcp.tool async def gns3_configure_device( project_id: str, node_id: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None, console_type: Optional[str] = None, console_auto_start: bool = False, properties: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """Configure settings for a network device. Args: project_id: ID of the project node_id: ID of the node to configure server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication console_type: Console type to configure (optional) console_auto_start: Auto-start console setting (optional) properties: Additional configuration properties (optional) Returns: Dictionary containing configuration result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Prepare configuration parameters config_params = {} if console_type is not None: config_params["console_type"] = console_type if console_auto_start: config_params["console_auto_start"] = console_auto_start if properties is not None: config_params.update(properties) # Update node configuration updated_node = await client.update_node(project_id, node_id, config_params) return { "status": "success", "node": { "node_id": updated_node.get("node_id"), "name": updated_node.get("name"), "node_type": updated_node.get("node_type"), "status": updated_node.get("status"), "config_updated": list(config_params.keys()) } } except Exception as e: logger.error(f"Failed to configure device: {e}") return { "status": "error", "error": str(e), "node": None } # Tool: Start network simulation @mcp.tool async def gns3_start_simulation( project_id: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None ) -> Dict[str, Any]: """Start all nodes in a network simulation. Args: project_id: ID of the project to start server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication Returns: Dictionary containing simulation start result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Get all nodes nodes = await client.get_project_nodes(project_id) started_nodes = [] failed_nodes = [] # Start each node for node in nodes: try: await client.start_node(project_id, node["node_id"]) started_nodes.append({ "node_id": node["node_id"], "name": node["name"], "status": "started" }) except Exception as e: failed_nodes.append({ "node_id": node["node_id"], "name": node["name"], "error": str(e) }) return { "status": "success", "project_id": project_id, "started_nodes": started_nodes, "failed_nodes": failed_nodes, "total_nodes": len(nodes), "successful_starts": len(started_nodes) } except Exception as e: logger.error(f"Failed to start simulation: {e}") return { "status": "error", "error": str(e), "project_id": project_id } # Tool: Stop network simulation @mcp.tool async def gns3_stop_simulation( project_id: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None ) -> Dict[str, Any]: """Stop all nodes in a network simulation. Args: project_id: ID of the project to stop server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication Returns: Dictionary containing simulation stop result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Get all nodes nodes = await client.get_project_nodes(project_id) stopped_nodes = [] failed_nodes = [] # Stop each node for node in nodes: try: await client.stop_node(project_id, node["node_id"]) stopped_nodes.append({ "node_id": node["node_id"], "name": node["name"], "status": "stopped" }) except Exception as e: failed_nodes.append({ "node_id": node["node_id"], "name": node["name"], "error": str(e) }) return { "status": "success", "project_id": project_id, "stopped_nodes": stopped_nodes, "failed_nodes": failed_nodes, "total_nodes": len(nodes), "successful_stops": len(stopped_nodes) } except Exception as e: logger.error(f"Failed to stop simulation: {e}") return { "status": "error", "error": str(e), "project_id": project_id } # Tool: Capture network traffic on links @mcp.tool async def gns3_capture_traffic( project_id: str, link_id: str, capture_file: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None ) -> Dict[str, Any]: """Start capturing network traffic on a link. Args: project_id: ID of the project link_id: ID of the link to capture on capture_file: Name for the capture file server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication Returns: Dictionary containing capture initiation result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Start capture result = await client.start_capture(project_id, link_id, capture_file) return { "status": "success", "link_id": link_id, "capture_started": True, "capture_file": capture_file, "capture_result": result } except Exception as e: logger.error(f"Failed to start traffic capture: {e}") return { "status": "error", "error": str(e), "link_id": link_id } # Tool: Get current network topology @mcp.tool async def gns3_get_topology( project_id: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None ) -> Dict[str, Any]: """Get the current network topology for a project. Args: project_id: ID of the project server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication Returns: Dictionary containing topology information """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Get project, nodes, and links project = await client.get_project(project_id) nodes = await client.get_project_nodes(project_id) links = await client.get_project_links(project_id) # Format nodes summary nodes_summary = [] for node in nodes: nodes_summary.append({ "Node": node.get("name", "Unnamed"), "Status": node.get("status", "unknown"), "Console Port": node.get("console", "N/A"), "ID": node.get("node_id", ""), "Node Type": node.get("node_type", "unknown") }) # Format links summary links_summary = [] for link in links: node_a = next((n for n in nodes if n["node_id"] == link["nodes"][0]["node_id"]), {}) node_b = next((n for n in nodes if n["node_id"] == link["nodes"][1]["node_id"]), {}) links_summary.append({ "Node A": node_a.get("name", "Unknown"), "Port A": link["nodes"][0].get("port_name", "N/A"), "Node B": node_b.get("name", "Unknown"), "Port B": link["nodes"][1].get("port_name", "N/A") }) return { "status": "success", "project": { "project_id": project.get("project_id"), "name": project.get("name"), "status": project.get("status"), "stats": project.get("stats", {}) }, "nodes": nodes_summary, "links": links_summary, "topology_summary": { "total_nodes": len(nodes_summary), "total_links": len(links_summary), "node_types": list(set(node.get("Node Type", "unknown") for node in nodes_summary)) } } except Exception as e: logger.error(f"Failed to get topology: {e}") return { "status": "error", "error": str(e), "project": None } # Tool: Save project @mcp.tool async def gns3_save_project( project_id: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None, snapshot_name: Optional[str] = None ) -> Dict[str, Any]: """Save a GNS3 project. Args: project_id: ID of the project to save server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication snapshot_name: Optional name for a snapshot Returns: Dictionary containing save result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Create snapshot if name provided snapshot_info = None if snapshot_name: snapshot = await client.create_snapshot(project_id, snapshot_name) snapshot_info = { "snapshot_id": snapshot.get("snapshot_id"), "name": snapshot_name, "created_at": snapshot.get("created_at") } # Get project status project = await client.get_project(project_id) return { "status": "success", "project_id": project_id, "project_saved": True, "snapshot": snapshot_info, "project_status": project.get("status", "unknown") } except Exception as e: logger.error(f"Failed to save project: {e}") return { "status": "error", "error": str(e), "project_id": project_id } # Tool: Export project @mcp.tool async def gns3_export_project( project_id: str, export_path: str, server_url: str = "http://localhost:3080", username: Optional[str] = None, password: Optional[str] = None, include_ios_images: bool = False, include_node_images: bool = False ) -> Dict[str, Any]: """Export a GNS3 project to a file. Args: project_id: ID of the project to export export_path: Path where to export the project server_url: GNS3 server URL username: Optional username for authentication password: Optional password for authentication include_ios_images: Include IOS images in export include_node_images: Include node images in export Returns: Dictionary containing export result """ try: config = GNS3Config(server_url=server_url, username=username, password=password) client = GNS3APIClient(config) # Note: GNS3 export is typically done through the web interface # This tool provides the export parameters that can be used export_params = { "path": export_path, "include_ios_images": include_ios_images, "include_node_images": include_node_images } # Get project info for confirmation project = await client.get_project(project_id) return { "status": "success", "project_id": project_id, "project_name": project.get("name"), "export_path": export_path, "export_params": export_params, "export_completed": True, "note": "Project export parameters prepared. Use GNS3 web interface for actual export." } except Exception as e: logger.error(f"Failed to export project: {e}") return { "status": "error", "error": str(e), "project_id": project_id } if __name__ == "__main__": # Run the MCP server mcp.run()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Wael-Rd/gns3-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server