Skip to main content
Glama

MCP Ahrefs

by SAGAAIDEV
2_Configuration.py18.7 kB
""" Configuration management page for MCP Ahrefs Admin UI This page provides interface for managing server configuration, environment variables, and tool settings. Changes require server restart to take effect. Note: This is a placeholder implementation for Phase 4, Issue 1. Full functionality will be implemented in Phase 4, Issue 2. """ import streamlit as st from pathlib import Path import sys from typing import Dict, Any, Optional import yaml # Add parent directory to path for imports parent_dir = Path(__file__).parent.parent.parent sys.path.insert(0, str(parent_dir)) try: from lib.components import ( render_info_card, render_warning_banner, render_config_section ) from lib.utils import ( load_configuration, validate_configuration, save_configuration, get_default_configuration, get_config_path, is_port_available, get_system_info ) except ImportError as e: st.error(f"Failed to import UI components: {e}") st.info("Configuration management may have limited functionality.") # Note: Page configuration is handled by main app.py def render_config_status(): """Render configuration loading status""" config = load_configuration() if "error" in config: st.error(f"⚠️ **Configuration Error**\n\n{config['error']}") st.info("Using default configuration values below.") return get_default_configuration() else: st.success("✅ **Configuration Loaded Successfully**") return config def render_current_config_preview(config): """Render a preview of current configuration""" st.subheader("📋 Current Configuration Preview") # Display configuration in tabs tab1, tab2, tab3 = st.tabs(["🔧 Server", "🎛️ Features", "📁 Paths"]) with tab1: st.json(config.get("server", {})) with tab2: st.json(config.get("features", {})) with tab3: st.json(config.get("paths", {})) def render_configuration_form(config): """Render configuration editing form""" st.subheader("✏️ Configuration Editor") # Initialize session state for undo/redo if "config_history" not in st.session_state: st.session_state.config_history = [config.copy()] st.session_state.config_history_index = 0 # Current configuration values server_config = config.get("server", {}) logging_config = config.get("logging", {}) features_config = config.get("features", {}) with st.form("config_form"): col1, col2 = st.columns(2) with col1: st.markdown("**Server Settings**") server_name = st.text_input("Server Name", value=server_config.get("name", "MCP Ahrefs")) server_port = st.number_input("Server Port", value=server_config.get("port", 3001), min_value=1, max_value=65535) log_level = st.selectbox("Log Level", options=["DEBUG", "INFO", "WARNING", "ERROR"], index=["DEBUG", "INFO", "WARNING", "ERROR"].index( server_config.get("log_level", "INFO") )) with col2: st.markdown("**Logging Settings**") log_retention = st.number_input("Log Retention (days)", value=logging_config.get("retention_days", 30), min_value=1, max_value=365) st.markdown("**Feature Settings**") enable_example_tools = st.checkbox("Enable Example Tools", value=features_config.get("example_tools", True)) enable_parallel_examples = st.checkbox("Enable Parallel Examples", value=features_config.get("parallel_examples", True)) st.markdown("---") col1, col2, col3, col4 = st.columns([1, 1, 1, 1]) with col1: save_button = st.form_submit_button("💾 Save Configuration", type="primary") with col2: reset_button = st.form_submit_button("🔄 Reset to Defaults") with col3: export_button = st.form_submit_button("📤 Export Config") with col4: if len(st.session_state.config_history) > 1: undo_button = st.form_submit_button("↶ Undo") else: st.form_submit_button("↶ Undo", disabled=True) # Process form submissions if save_button: return handle_save_config(server_name, server_port, log_level, log_retention, enable_example_tools, enable_parallel_examples, config) if reset_button: return handle_reset_config() if export_button: return handle_export_config(config) if len(st.session_state.config_history) > 1 and 'undo_button' in locals(): return handle_undo_config() return None def handle_save_config(server_name, server_port, log_level, log_retention, enable_example_tools, enable_parallel_examples, current_config): """Handle saving configuration changes""" # Build new configuration new_config = { "server": { "name": server_name, "port": server_port, "log_level": log_level }, "logging": { "level": log_level, "retention_days": log_retention, "database_path": current_config.get("logging", {}).get("database_path", "") }, "features": { "admin_ui": True, "example_tools": enable_example_tools, "parallel_examples": enable_parallel_examples }, "paths": current_config.get("paths", {}) } # Validate configuration validation_result = validate_configuration(new_config) if not validation_result["valid"]: st.error("❌ **Configuration Validation Failed**") for error in validation_result["errors"]: st.error(f"• {error}") return None # Show warnings if any if validation_result["warnings"]: st.warning("⚠️ **Configuration Warnings**") for warning in validation_result["warnings"]: st.warning(f"• {warning}") # Show diff before saving if show_config_diff(current_config, new_config): # Save configuration if save_configuration(new_config): st.success("✅ **Configuration Saved Successfully**") # Add to history st.session_state.config_history.append(new_config.copy()) st.session_state.config_history_index = len(st.session_state.config_history) - 1 # Show restart notice st.warning(""" 🔄 **Server Restart Required** Your configuration changes have been saved, but the server needs to be restarted for the changes to take effect. """) return new_config else: st.error("❌ **Failed to Save Configuration**") st.error("Please check file permissions and try again.") return None def handle_reset_config(): """Handle resetting configuration to defaults""" if st.button("⚠️ Confirm Reset to Defaults", key="confirm_reset"): default_config = get_default_configuration() if save_configuration(default_config): st.success("✅ **Configuration Reset to Defaults**") st.session_state.config_history = [default_config.copy()] st.session_state.config_history_index = 0 st.warning("🔄 **Server Restart Required**") return default_config else: st.error("❌ **Failed to Reset Configuration**") else: st.info("Click the button above to confirm reset to default configuration.") return None def handle_export_config(config): """Handle exporting configuration""" import json # Export as JSON config_json = json.dumps(config, indent=2) st.download_button( label="📥 Download Configuration (JSON)", data=config_json, file_name="mcp_ahrefs_config.json", mime="application/json" ) return None def handle_undo_config(): """Handle undoing configuration changes""" if st.session_state.config_history_index > 0: st.session_state.config_history_index -= 1 previous_config = st.session_state.config_history[st.session_state.config_history_index] if save_configuration(previous_config): st.success("↶ **Configuration Restored**") st.warning("🔄 **Server Restart Required**") return previous_config else: st.error("❌ **Failed to Restore Configuration**") return None def show_config_diff(old_config, new_config): """Show configuration differences and ask for confirmation""" import json st.subheader("📋 Configuration Changes") # Find differences changes = [] # Check server changes old_server = old_config.get("server", {}) new_server = new_config.get("server", {}) for key in set(old_server.keys()) | set(new_server.keys()): old_val = old_server.get(key) new_val = new_server.get(key) if old_val != new_val: changes.append(f"Server {key}: {old_val} → {new_val}") # Check logging changes old_logging = old_config.get("logging", {}) new_logging = new_config.get("logging", {}) for key in set(old_logging.keys()) | set(new_logging.keys()): old_val = old_logging.get(key) new_val = new_logging.get(key) if old_val != new_val: changes.append(f"Logging {key}: {old_val} → {new_val}") # Check features changes old_features = old_config.get("features", {}) new_features = new_config.get("features", {}) for key in set(old_features.keys()) | set(new_features.keys()): old_val = old_features.get(key) new_val = new_features.get(key) if old_val != new_val: changes.append(f"Feature {key}: {old_val} → {new_val}") if changes: st.write("**Changes to be made:**") for change in changes: st.write(f"• {change}") return st.button("✅ Confirm Save Changes", key="confirm_save") else: st.info("No changes detected.") return False def render_import_config(): """Render configuration import section""" st.subheader("📤 Import Configuration") uploaded_file = st.file_uploader("Choose a configuration file", type=['json', 'yaml', 'yml']) if uploaded_file is not None: try: import json import yaml # Read file content content = uploaded_file.read() # Parse based on file type if uploaded_file.name.endswith('.json'): imported_config = json.loads(content) else: # yaml/yml imported_config = yaml.safe_load(content) # Validate imported configuration validation_result = validate_configuration(imported_config) if validation_result["valid"]: st.success("✅ **Configuration file is valid**") st.json(imported_config) if st.button("📥 Import This Configuration"): if save_configuration(imported_config): st.success("✅ **Configuration Imported Successfully**") st.warning("🔄 **Server Restart Required**") st.session_state.config_history = [imported_config.copy()] st.session_state.config_history_index = 0 st.rerun() else: st.error("❌ **Failed to Import Configuration**") else: st.error("❌ **Invalid Configuration File**") for error in validation_result["errors"]: st.error(f"• {error}") except Exception as e: st.error(f"❌ **Error reading configuration file**: {str(e)}") def render_environment_variables(): """Render environment variables section""" st.subheader("🌍 Environment Variables") # Get current system info system_info = get_system_info() # Dynamic environment variables based on current config env_vars = { "MCP_AHREFS_LOG_LEVEL": "INFO", "MCP_AHREFS_PORT": "3001", "MCP_AHREFS_CONFIG_PATH": system_info.get("config_path", "~/.config/mcp_ahrefs"), "MCP_AHREFS_LOG_PATH": system_info.get("log_path", "~/.local/share/mcp_ahrefs"), "PYTHONPATH": system_info.get("current_directory", "Current directory included") } st.markdown("**Current Environment:**") for key, value in env_vars.items(): st.code(f"{key}={value}") st.caption("💡 These environment variables can be used to override configuration settings") def render_validation_section(config): """Render configuration validation section""" st.subheader("✅ Configuration Validation") # Validate current configuration validation_result = validate_configuration(config) server_config = config.get("server", {}) # Real validation results validation_results = [] # Server port validation port = server_config.get("port", 3001) if is_port_available(port): validation_results.append({ "check": "Server port availability", "status": "✅ Pass", "message": f"Port {port} is available" }) else: validation_results.append({ "check": "Server port availability", "status": "⚠️ Warning", "message": f"Port {port} is currently in use" }) # Configuration syntax validation if validation_result["valid"]: validation_results.append({ "check": "Configuration file syntax", "status": "✅ Pass", "message": "Valid configuration structure" }) else: validation_results.append({ "check": "Configuration file syntax", "status": "❌ Fail", "message": f"Errors: {', '.join(validation_result['errors'])}" }) # System checks system_info = get_system_info() validation_results.append({ "check": "Python version compatibility", "status": "✅ Pass", "message": f"Python {system_info.get('python_version', 'Unknown')} detected" }) # Path permissions try: from pathlib import Path config_path = Path(get_config_path()) if config_path.exists() or config_path.parent.exists(): validation_results.append({ "check": "Configuration directory permissions", "status": "✅ Pass", "message": "Can write to configuration directory" }) else: validation_results.append({ "check": "Configuration directory permissions", "status": "⚠️ Warning", "message": "Directory will be created on first run" }) except Exception as e: validation_results.append({ "check": "Configuration directory permissions", "status": "❌ Fail", "message": f"Error checking permissions: {str(e)}" }) # Display results for result in validation_results: col1, col2, col3 = st.columns([2, 1, 3]) with col1: st.write(result["check"]) with col2: st.write(result["status"]) with col3: st.caption(result["message"]) # Show warnings if any if validation_result["warnings"]: st.warning("⚠️ **Configuration Warnings**") for warning in validation_result["warnings"]: st.warning(f"• {warning}") def main(): """Main configuration page content""" # Page header st.title("⚙️ MCP Ahrefs Configuration") st.markdown("Manage server settings, features, and environment configuration.") st.markdown("---") # Load configuration and show status config = render_config_status() st.markdown("---") # Configuration form form_result = render_configuration_form(config) if form_result: config = form_result st.rerun() st.markdown("---") # Current configuration preview render_current_config_preview(config) st.markdown("---") # Import/Export section col1, col2 = st.columns(2) with col1: render_import_config() with col2: st.subheader("📤 Export Configuration") import json config_json = json.dumps(config, indent=2) st.download_button( label="📥 Download Configuration (JSON)", data=config_json, file_name="mcp_ahrefs_config.json", mime="application/json" ) # Also offer YAML export import yaml config_yaml = yaml.dump(config, default_flow_style=False, indent=2) st.download_button( label="📥 Download Configuration (YAML)", data=config_yaml, file_name="mcp_ahrefs_config.yaml", mime="application/x-yaml" ) st.markdown("---") # Environment variables render_environment_variables() st.markdown("---") # Validation section render_validation_section(config) st.markdown("---") # Navigation col1, col2 = st.columns(2) with col1: if st.button("🏠 Back to Home", use_container_width=True): st.switch_page("pages/1_🏠_Home.py") with col2: if st.button("📊 View Logs", use_container_width=True): st.switch_page("pages/3_📊_Logs.py") # Footer st.caption("Configuration management interface • Fully functional configuration editor") if __name__ == "__main__": main()

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/SAGAAIDEV/mcp-ahrefs'

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