# πŸ”— Integration Patterns: Arduino MCP ↔ ESPTool MCP ## Overview The ESPTool MCP server is designed to complement and enhance the existing Arduino MCP server, creating a unified ESP development ecosystem. Rather than replacing Arduino workflows, it provides specialized ESP capabilities that work seamlessly together. ## 🎯 Integration Philosophy ### Complementary Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Arduino MCP β”‚ β”‚ ESPTool MCP β”‚ β”‚ Server β”‚ β”‚ Server β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β€’ Sketch Management β”‚ β”‚ β€’ Direct ESP Controlβ”‚ β”‚ β€’ Library System β”‚ β”‚ β€’ Advanced Flashing β”‚ β”‚ β€’ Compilation β”‚ β”‚ β€’ Security Features β”‚ β”‚ β€’ Basic Upload β”‚ β”‚ β€’ Production Tools β”‚ β”‚ β€’ Serial Monitor β”‚ β”‚ β€’ OTA Management β”‚ β”‚ β€’ Cross-Platform β”‚ β”‚ β€’ eFuse Programming β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Unified ESP β”‚ β”‚ Development β”‚ β”‚ Workflow β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## πŸ—οΈ Integration Patterns ### 1. **Workflow Handoff Pattern** The Arduino MCP server handles high-level development, while ESPTool MCP handles low-level chip operations: ```python # Example: Advanced ESP32 Development Workflow class UnifiedESPWorkflow: """Orchestrates between Arduino and ESPTool MCP servers""" async def complete_esp_development_cycle(self, project_config: dict): """End-to-end ESP development using both servers""" # Phase 1: Arduino MCP Server - High-level development arduino_tasks = [ "arduino_create_sketch", # Create project structure "arduino_install_library", # Install ESP32 libraries "arduino_write_sketch", # Write application code "arduino_compile_sketch" # Compile to binary ] # Phase 2: ESPTool MCP Server - Low-level optimization esptool_tasks = [ "esp_detect_chip", # Identify ESP variant "esp_firmware_analyze", # Optimize binary "esp_partition_create_ota", # Setup OTA partitions "esp_flash_firmware", # Advanced flashing "esp_security_audit" # Security validation ] return await self._execute_unified_workflow(arduino_tasks, esptool_tasks) ``` ### 2. **Shared Configuration Pattern** Both servers share common configuration for seamless operation: ```python # config/unified_esp_config.py from pathlib import Path from dataclasses import dataclass from typing import List, Dict @dataclass class UnifiedESPConfig: """Shared configuration between Arduino and ESPTool MCP servers""" # Shared paths project_roots: List[Path] sketch_directory: Path libraries_directory: Path tools_directory: Path # Arduino MCP specific arduino_cli_path: str arduino_cores: List[str] # ESPTool MCP specific esptool_path: str esp_idf_path: Path partition_templates: Dict[str, Path] # Shared ESP settings default_baud_rate: int = 460800 connection_timeout: int = 30 enable_stub_flasher: bool = True @classmethod def from_environment(cls) -> 'UnifiedESPConfig': """Initialize from environment variables and MCP roots""" return cls( project_roots=cls._get_mcp_roots(), sketch_directory=Path(os.getenv('MCP_SKETCH_DIR', '~/Arduino')).expanduser(), arduino_cli_path=os.getenv('ARDUINO_CLI_PATH', 'arduino-cli'), esptool_path=os.getenv('ESPTOOL_PATH', 'esptool'), # ... other config ) ``` ### 3. **Cross-Server Communication Pattern** Enable servers to coordinate operations and share information: ```python # coordination/server_bridge.py class MCPServerBridge: """Facilitates communication between Arduino and ESPTool MCP servers""" def __init__(self, arduino_server_url: str, esptool_server_url: str): self.arduino_client = MCPClient(arduino_server_url) self.esptool_client = MCPClient(esptool_server_url) async def compile_and_flash_esp(self, sketch_path: str, port: str) -> dict: """Coordinate compilation via Arduino MCP and flashing via ESPTool MCP""" # Step 1: Use Arduino MCP to compile compile_result = await self.arduino_client.call_tool( "arduino_compile_sketch", {"sketch_path": sketch_path, "board": "esp32"} ) if not compile_result["success"]: return {"error": "Compilation failed", "details": compile_result} # Step 2: Use ESPTool MCP for advanced flashing flash_result = await self.esptool_client.call_tool( "esp_flash_firmware", { "port": port, "firmware_files": [{"address": 0x1000, "file": compile_result["binary_path"]}], "verify": True, "optimize": True } ) return { "compile_result": compile_result, "flash_result": flash_result, "success": flash_result.get("success", False) } ``` ## 🎨 Natural Language Workflows ### Unified Development Commands Claude can seamlessly use both servers to provide comprehensive ESP development: ``` User: "Create an ESP32 weather station with OTA updates" Claude: I'll create a comprehensive ESP32 weather station with OTA capability using both Arduino and ESP-specific tools. # Arduino MCP Server - Application Development β†’ arduino_create_sketch("WeatherStation") β†’ arduino_install_library("DHT sensor library") β†’ arduino_install_library("WiFiManager") β†’ arduino_write_sketch(weather_station_code) β†’ arduino_compile_sketch() # ESPTool MCP Server - Production Setup β†’ esp_detect_chip(port="/dev/ttyUSB0") β†’ esp_partition_create_ota(app_size="1MB") β†’ esp_flash_firmware(with_ota_partitions=True) β†’ esp_security_audit() βœ“ Weather station deployed with OTA updates enabled βœ“ Secure boot configured for production βœ“ Ready for remote firmware updates ``` ### Development vs Production Workflows ``` User: "I'm developing an ESP32 project - I want fast iteration" Claude: I'll set up a development-optimized workflow for rapid testing. # Development Mode - Fast Iteration β†’ arduino_compile_sketch(board="esp32", debug=True) β†’ esp_load_test_firmware(to_ram=True) # No flash wear β†’ serial_monitor(detailed_logging=True) # When ready for testing: β†’ esp_flash_firmware(development_mode=True) User: "Now I'm ready for production deployment" Claude: I'll configure this for production with security and optimization. # Production Mode - Security & Optimization β†’ esp_firmware_analyze(optimize=True) β†’ esp_enable_flash_encryption() β†’ esp_enable_secure_boot() β†’ esp_flash_firmware(production_mode=True) β†’ esp_factory_test_suite() βœ“ Production deployment complete with security enabled ``` ## πŸ”„ Shared Resource Management ### Unified Device Detection ```python # resources/unified_device_manager.py class UnifiedDeviceManager: """Manages ESP devices across both MCP servers""" async def get_all_esp_devices(self) -> List[Dict]: """Combine device information from both servers""" # Get basic device info from Arduino MCP arduino_devices = await self._get_arduino_devices() # Get detailed ESP info from ESPTool MCP esptool_devices = await self._get_esptool_devices() # Merge information unified_devices = [] for device in arduino_devices: if device['board_type'].startswith('esp'): esp_details = self._find_esptool_device(device['port'], esptool_devices) unified_devices.append({ **device, "esp_details": esp_details, "capabilities": self._determine_capabilities(device, esp_details) }) return unified_devices def _determine_capabilities(self, arduino_info: dict, esp_info: dict) -> List[str]: """Determine what operations are available for this device""" capabilities = ["compile", "upload", "monitor"] # Arduino basics if esp_info: capabilities.extend([ "direct_flash", "partition_management", "security_config", "ota_support", "factory_programming" ]) return capabilities ``` ### Resource Synchronization ```python # Both servers expose unified resources @arduino_mcp.resource("unified://esp_devices") @esptool_mcp.resource("unified://esp_devices") async def synchronized_esp_devices() -> str: """Synchronized device list across both servers""" device_manager = UnifiedDeviceManager() devices = await device_manager.get_all_esp_devices() return json.dumps(devices, indent=2) ``` ## πŸš€ Advanced Integration Scenarios ### 1. **CI/CD Pipeline Integration** ```yaml # .github/workflows/esp32-deploy.yml name: ESP32 Production Deployment on: push: tags: ['v*'] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup ESP Development Environment run: | # Install both MCP servers uvx mcp-arduino uvx mcp-esptool-server - name: Compile Firmware run: | claude mcp call arduino arduino_compile_sketch \ --sketch-path ./WeatherStation \ --board esp32 \ --optimize-size - name: Security Configuration run: | claude mcp call esptool esp_firmware_analyze \ --input ./WeatherStation/build/WeatherStation.bin \ --security-check - name: Factory Programming run: | claude mcp call esptool esp_factory_program \ --firmware ./WeatherStation/build/WeatherStation.bin \ --security-keys ./keys/ \ --partition-table ./partitions/production.csv ``` ### 2. **Development Environment Setup** ```python # setup/unified_development_environment.py class UnifiedESPDevEnvironment: """Sets up complete ESP development environment""" async def setup_project(self, project_name: str, project_type: str): """Initialize new ESP project with both Arduino and ESP tooling""" setup_tasks = { "arduino_tasks": [ ("arduino_create_sketch", {"name": project_name}), ("arduino_install_core", {"core": "esp32"}), ("arduino_config_init", {}) ], "esptool_tasks": [ ("esp_config_init", {}), ("esp_partition_create_ota", {"project": project_name}), ("esp_security_template_create", {"level": "development"}) ] } # Execute tasks in parallel where possible arduino_results = await self._execute_arduino_tasks(setup_tasks["arduino_tasks"]) esptool_results = await self._execute_esptool_tasks(setup_tasks["esptool_tasks"]) return { "project_name": project_name, "arduino_setup": arduino_results, "esptool_setup": esptool_results, "ready_for_development": all([arduino_results["success"], esptool_results["success"]]) } ``` ### 3. **Error Recovery and Diagnostics** ```python # diagnostics/unified_troubleshooting.py class UnifiedTroubleshooting: """Cross-server diagnostic and recovery tools""" async def diagnose_esp_issue(self, port: str, symptoms: List[str]) -> dict: """Use both servers to diagnose ESP development issues""" diagnosis = { "port": port, "symptoms": symptoms, "tests_performed": [], "recommendations": [] } # Arduino MCP diagnostics arduino_tests = [ ("serial_check_port", {"port": port}), ("arduino_list_boards", {}), ("serial_test_connection", {"port": port}) ] # ESPTool MCP diagnostics esptool_tests = [ ("esp_detect_chip", {"port": port}), ("esp_security_audit", {"port": port}), ("esp_flash_analyze", {"port": port}) ] # Run diagnostics and generate recommendations arduino_results = await self._run_arduino_diagnostics(arduino_tests) esptool_results = await self._run_esptool_diagnostics(esptool_tests) diagnosis.update({ "arduino_diagnostics": arduino_results, "esptool_diagnostics": esptool_results, "recommendations": self._generate_recommendations(arduino_results, esptool_results) }) return diagnosis ``` This integration design ensures that both MCP servers work together seamlessly, providing Claude with a comprehensive toolkit for ESP development that spans from high-level Arduino sketch development to low-level chip programming and production deployment.