#!/usr/bin/env nu # Integration Functions for External Systems # # Provides integration with: # - MCP (Model Context Protocol) servers # - Rust installer binary # - REST APIs # - Webhook notifications # Load configuration from MCP server # # Queries the MCP server for deployment configuration using # the Model Context Protocol. # # @param mcp_url: MCP server URL # @returns: Deployment configuration record export def load-config-from-mcp [mcp_url: string]: nothing -> record { print $"📡 Loading configuration from MCP server: ($mcp_url)" # MCP request payload let request = { jsonrpc: "2.0" id: 1 method: "config/get" params: { type: "deployment" include_defaults: true } } try { let response = ( http post $mcp_url --content-type "application/json" ($request | to json) ) if "error" in ($response | columns) { error make { msg: $"MCP error: ($response.error.message)" label: {text: $"Code: ($response.error.code)"} } } if "result" not-in ($response | columns) { error make {msg: "Invalid MCP response: missing result"} } print "✅ Configuration loaded from MCP server" $response.result } catch {|err| error make { msg: $"Failed to load config from MCP: ($mcp_url)" label: {text: $err.msg} help: "Ensure MCP server is running and accessible" } } } # Load configuration from REST API # # Fetches deployment configuration from a REST API endpoint. # # @param api_url: API endpoint URL # @returns: Deployment configuration record export def load-config-from-api [api_url: string]: nothing -> record { print $"🌐 Loading configuration from API: ($api_url)" try { let response = (http get $api_url --max-time 30sec) if "config" not-in ($response | columns) { error make {msg: "Invalid API response: missing 'config' field"} } print "✅ Configuration loaded from API" $response.config } catch {|err| error make { msg: $"Failed to load config from API: ($api_url)" label: {text: $err.msg} help: "Check API endpoint and network connectivity" } } } # Send notification to webhook # # Sends deployment event notifications to a configured webhook URL. # Useful for integration with Slack, Discord, Microsoft Teams, etc. # # @param webhook_url: Webhook URL # @param payload: Notification payload record # @returns: Nothing export def notify-webhook [webhook_url: string, payload: record]: nothing -> nothing { try { http post $webhook_url --content-type "application/json" ($payload | to json) null } catch {|err| # Don't fail deployment on webhook errors, just log print $"⚠️ Warning: Failed to send webhook notification: ($err.msg)" null } } # Call Rust installer binary with arguments # # Invokes the Rust installer binary with specified arguments, # capturing output and exit code. # # @param args: List of arguments to pass to installer # @returns: Installer execution result record export def call-installer [args: list]: nothing -> record { let installer_path = get-installer-path print $"🚀 Calling installer: ($installer_path) ($args | str join ' ')" try { let output = (^$installer_path ...$args | complete) { success: ($output.exit_code == 0) exit_code: $output.exit_code stdout: $output.stdout stderr: $output.stderr timestamp: (date now) } } catch {|err| { success: false exit_code: -1 error: $err.msg timestamp: (date now) } } } # Run installer in headless mode with config file # # Executes the Rust installer in headless mode using a # configuration file. # # @param config_path: Path to configuration file # @param auto_confirm: Auto-confirm prompts # @returns: Installer execution result record export def run-installer-headless [ config_path: path --auto-confirm ]: nothing -> record { mut args = ["--headless", "--config", $config_path] if $auto_confirm { $args = ($args | append "--yes") } call-installer $args } # Run installer interactively # # Launches the Rust installer in interactive TUI mode. # # @returns: Installer execution result record export def run-installer-interactive []: nothing -> record { let installer_path = get-installer-path print $"🚀 Launching interactive installer: ($installer_path)" try { # Run without capturing output (interactive mode) ^$installer_path { success: true mode: "interactive" message: "Interactive installer completed" timestamp: (date now) } } catch {|err| { success: false mode: "interactive" error: $err.msg timestamp: (date now) } } } # Pass deployment config to installer via CLI args # # Converts a deployment configuration record into CLI arguments # for the Rust installer binary. # # @param config: Deployment configuration record # @returns: List of CLI arguments export def config-to-cli-args [config: record]: nothing -> list { mut args = ["--headless"] # Add platform $args = ($args | append ["--platform", $config.platform]) # Add mode $args = ($args | append ["--mode", $config.mode]) # Add domain $args = ($args | append ["--domain", $config.domain]) # Add services (comma-separated) let services = $config.services | where enabled | get name | str join "," if $services != "" { $args = ($args | append ["--services", $services]) } $args } # Deploy using installer binary # # High-level function to deploy using the Rust installer binary # with the given configuration. # # @param config: Deployment configuration record # @param auto_confirm: Auto-confirm prompts # @returns: Deployment result record export def deploy-with-installer [ config: record --auto-confirm ]: nothing -> record { print "🚀 Deploying using Rust installer binary..." # Convert config to CLI args mut args = (config-to-cli-args $config) if $auto_confirm { $args = ($args | append "--yes") } # Execute installer let result = call-installer $args if $result.success { print "✅ Installer deployment successful" { success: true method: "installer_binary" config: $config timestamp: (date now) } } else { print $"❌ Installer deployment failed: ($result.stderr)" { success: false method: "installer_binary" error: $result.stderr exit_code: $result.exit_code timestamp: (date now) } } } # Query MCP server for deployment status # # Retrieves deployment status information from MCP server. # # @param mcp_url: MCP server URL # @param deployment_id: Deployment identifier # @returns: Deployment status record export def query-mcp-status [mcp_url: string, deployment_id: string]: nothing -> record { let request = { jsonrpc: "2.0" id: 1 method: "deployment/status" params: { deployment_id: $deployment_id } } try { let response = ( http post $mcp_url --content-type "application/json" ($request | to json) ) if "error" in ($response | columns) { error make { msg: $"MCP error: ($response.error.message)" } } $response.result } catch {|err| error make { msg: $"Failed to query MCP status: ($err.msg)" } } } # Register deployment with API # # Registers a new deployment with the external API and returns # a deployment ID for tracking. # # @param api_url: API endpoint URL # @param config: Deployment configuration # @returns: Registration result with deployment ID export def register-deployment-with-api [api_url: string, config: record]: nothing -> record { let payload = { platform: $config.platform mode: $config.mode domain: $config.domain services: ($config.services | get name) started_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ") } try { let response = ( http post $api_url --content-type "application/json" ($payload | to json) ) if "deployment_id" not-in ($response | columns) { error make {msg: "API did not return deployment_id"} } print $"✅ Deployment registered with API: ($response.deployment_id)" { success: true deployment_id: $response.deployment_id api_url: $api_url } } catch {|err| print $"⚠️ Warning: Failed to register with API: ($err.msg)" { success: false error: $err.msg } } } # Update deployment status via API # # Updates deployment status on external API for tracking and monitoring. # # @param api_url: API endpoint URL # @param deployment_id: Deployment identifier # @param status: Status update record # @returns: Update result record export def update-deployment-status [ api_url: string deployment_id: string status: record ]: nothing -> record { let update_url = $"($api_url)/($deployment_id)/status" try { http patch $update_url --content-type "application/json" ($status | to json) {success: true} } catch {|err| print $"⚠️ Warning: Failed to update deployment status: ($err.msg)" {success: false, error: $err.msg} } } # Send Slack notification # # Sends formatted notification to Slack webhook. # # @param webhook_url: Slack webhook URL # @param message: Message text # @param color: Message color (good, warning, danger) # @returns: Nothing export def notify-slack [ webhook_url: string message: string --color: string = "good" ]: nothing -> nothing { let payload = { attachments: [{ color: $color text: $message footer: "Provisioning Platform Installer" ts: (date now | format date "%s") }] } notify-webhook $webhook_url $payload } # Send Discord notification # # Sends formatted notification to Discord webhook. # # @param webhook_url: Discord webhook URL # @param message: Message text # @param success: Whether deployment was successful # @returns: Nothing export def notify-discord [ webhook_url: string message: string --success ]: nothing -> nothing { let color = if $success { 3066993 } else { 15158332 } # Green or Red let emoji = if $success { "✅" } else { "❌" } let payload = { embeds: [{ title: $"($emoji) Provisioning Platform Deployment" description: $message color: $color timestamp: (date now | format date "%Y-%m-%dT%H:%M:%SZ") footer: { text: "Provisioning Platform Installer" } }] } notify-webhook $webhook_url $payload } # Send Microsoft Teams notification # # Sends formatted notification to Microsoft Teams webhook. # # @param webhook_url: Teams webhook URL # @param title: Notification title # @param message: Message text # @param success: Whether deployment was successful # @returns: Nothing export def notify-teams [ webhook_url: string title: string message: string --success ]: nothing -> nothing { let theme_color = if $success { "00FF00" } else { "FF0000" } let payload = { "@type": "MessageCard" "@context": "https://schema.org/extensions" summary: $title themeColor: $theme_color title: $title text: $message } notify-webhook $webhook_url $payload } # Execute MCP tool call # # Executes a tool/function call via MCP server. # # @param mcp_url: MCP server URL # @param tool_name: Name of tool to execute # @param arguments: Tool arguments record # @returns: Tool execution result export def execute-mcp-tool [ mcp_url: string tool_name: string arguments: record ]: nothing -> record { let request = { jsonrpc: "2.0" id: 1 method: "tools/call" params: { name: $tool_name arguments: $arguments } } try { let response = ( http post $mcp_url --content-type "application/json" ($request | to json) ) if "error" in ($response | columns) { error make { msg: $"MCP tool execution error: ($response.error.message)" } } $response.result } catch {|err| error make { msg: $"Failed to execute MCP tool: ($err.msg)" } } } # Get installer binary path (helper function) # # @returns: Path to installer binary def get-installer-path []: nothing -> path { let installer_dir = $env.PWD | path dirname let installer_name = if $nu.os-info.name == "windows" { "provisioning-installer.exe" } else { "provisioning-installer" } # Check target/release first, then target/debug let release_path = $installer_dir | path join "target" "release" $installer_name let debug_path = $installer_dir | path join "target" "debug" $installer_name if ($release_path | path exists) { $release_path } else if ($debug_path | path exists) { $debug_path } else { error make { msg: "Installer binary not found" help: "Build with: cargo build --release" } } }