"""Excalidraw MCP Server Integration. Manages the external excalidraw-mcp server process for hand-drawn style diagram generation. Architecture: Garvis → (stdio) → excalidraw-mcp (Node.js) → Canvas API → PNG/SVG images """ import os from pathlib import Path from typing import Any, Dict, List import yaml _CONFIG_FILE = Path("config/excalidraw_mcp.yaml") def _load_config() -> Dict[str, Any]: """Load Excalidraw MCP configuration from YAML and env vars.""" config = {} if _CONFIG_FILE.exists(): try: with open(_CONFIG_FILE, encoding="utf-8") as f: config = yaml.safe_load(f) or {} except Exception: pass # Use defaults if config fails to load excalidraw = config.get("excalidraw_mcp", {}) # Apply env var overrides if os.getenv("EXCALIDRAW_ENABLED"): excalidraw["enabled"] = os.getenv("EXCALIDRAW_ENABLED").lower() in ("true", "1") if os.getenv("EXCALIDRAW_OUTPUT_DIR"): excalidraw["output_dir"] = os.getenv("EXCALIDRAW_OUTPUT_DIR") if os.getenv("EXCALIDRAW_DEFAULT_EXPORT"): excalidraw["default_export_type"] = os.getenv("EXCALIDRAW_DEFAULT_EXPORT") # Set defaults if not configured excalidraw.setdefault("enabled", True) excalidraw.setdefault("output_dir", "downloads/diagrams/excalidraw") excalidraw.setdefault("default_export_type", "png") excalidraw.setdefault("canvas_width", 1920) excalidraw.setdefault("canvas_height", 1080) return excalidraw def is_excalidraw_enabled() -> bool: """Check if Excalidraw MCP integration is enabled.""" config = _load_config() return config.get("enabled", True) def get_excalidraw_server_config() -> Dict[str, Any]: """Build the MCP server configuration for Agent SDK registration. Returns: Dict with command, args, and env for subprocess execution """ config = _load_config() output_dir = config.get("output_dir", "downloads/diagrams/excalidraw") # Ensure output directory exists Path(output_dir).mkdir(parents=True, exist_ok=True) # Build environment variables for the MCP server env = { "PATH": os.environ.get("PATH", ""), "HOME": os.environ.get("HOME", os.environ.get("USERPROFILE", "")), "APPDATA": os.environ.get("APPDATA", ""), "TEMP": os.environ.get("TEMP", os.environ.get("TMP", "")), } # Add config as env vars for the server env["EXCALIDRAW_OUTPUT_DIR"] = str(Path(output_dir).absolute()) env["EXCALIDRAW_DEFAULT_EXPORT"] = config.get("default_export_type", "png") env["EXCALIDRAW_CANVAS_WIDTH"] = str(config.get("canvas_width", 1920)) env["EXCALIDRAW_CANVAS_HEIGHT"] = str(config.get("canvas_height", 1080)) return { "command": "npx", "args": ["-y", "excalidraw-mcp"], "env": env, } # Tool names exposed by excalidraw-mcp # Based on excalidraw-mcp documentation EXCALIDRAW_TOOLS: List[str] = [ "create_diagram", # Create new Excalidraw diagram "add_element", # Add shape/element to diagram "export_diagram", # Export to PNG/SVG "list_diagrams", # List saved diagrams ]