import api_client import json from datetime import datetime, timedelta import hashlib import psycopg2 import os from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() def validate_api_configuration( mcp_api_key, name, description, method, base_url, endpoint, param_keys_values, header_keys_values, additional_params, schedule_interval_minutes, stop_after_hours, time_to_start, ): """ TOOL: Validate and store API configuration for monitoring. PURPOSE: Test an API endpoint and store the configuration if successful. This is STEP 1 of the monitoring setup process. If validation fails, retry with corrected parameters. If successful, use the returned config_id in activate_monitoring() function. ⚠️ CRITICAL: Even if success=True, you MUST manually check the 'sample_response' field before proceeding to activate_monitoring(). The API call may return success=True but contain error messages (like "401 Unauthorized", "Invalid API key", etc.) in the sample_response. WORKFLOW: 1. Call this function to validate API configuration 2. If success=False: Fix parameters and retry this function 3. If success=True: MANUALLY INSPECT the 'sample_response' field for errors 4. If sample_response contains error messages: Fix API parameters and retry validation 5. If sample_response looks valid: Use config_id in activate_monitoring() to activate monitoring Parameters: - mcp_api_key: MCP API key serves as user identifier - name: User-friendly name for the monitoring task - description: Description of what is being monitored - method: HTTP method (GET, POST, PUT, DELETE) - base_url: The base URL of the API - endpoint: The specific API endpoint - param_keys_values: Parameter key-value pairs, one per line - header_keys_values: Header key-value pairs, one per line - additional_params: Optional JSON string for complex parameters - schedule_interval_minutes: Minutes between calls - stop_after_hours: Hours after which to stop (max 168 = 1 week) - time_to_start: When to start the monitoring (datetime string or None for immediate) Input Examples: 1. Simple GET request to monitor stock price: mcp_api_key: "your_mcp_key_here" name: "NVDA Stock Price" description: "Monitor NVIDIA stock price every 30 minutes" method: "GET" base_url: "https://api.example.com" endpoint: "stocks/NVDA" param_keys_values: "symbol: NVDA\ninterval: 1min" header_keys_values: "Authorization: Bearer your_token" additional_params: "{}" schedule_interval_minutes: 30 stop_after_hours: 24 time_to_start: "" 2. API with complex parameters: mcp_api_key: "your_mcp_key_here" name: "Weather Alert Monitor" description: "Monitor severe weather alerts" method: "POST" base_url: "https://api.weather.com" endpoint: "alerts" param_keys_values: "lat: 40.7128\nlon: -74.0060" header_keys_values: "X-API-Key: weather_key\nContent-Type: application/json" additional_params: '{"severity": ["severe", "extreme"], "types": ["tornado", "hurricane"]}' schedule_interval_minutes: 15 stop_after_hours: 48 time_to_start: "2024-06-15 09:00:00" Returns: - Dictionary with success status, config_id (needed for setup_scheduler), message, and sample_response Example return: { "success": True, "config_id": 123, "message": "API call tested and stored successfully", "sample_response": {...}, "stop_at": "2025-06-11T12:00:00Z", "start_at": "2025-06-04T12:00:00Z" } NEXT STEP: If success=True, call activate_monitoring(config_id, mcp_api_key) to activate monitoring """ try: # Validate input parameters if not mcp_api_key or not mcp_api_key.strip(): return { "success": False, "message": "MCP API key is required", "config_id": None, } if not name or not name.strip(): return { "success": False, "message": "Monitoring name is required", "config_id": None, } if not base_url or not base_url.strip(): return { "success": False, "message": "Base URL is required", "config_id": None, } if not method or method not in ["GET", "POST", "PUT", "DELETE"]: return { "success": False, "message": "Valid HTTP method is required (GET, POST, PUT, DELETE)", "config_id": None, } if ( not isinstance(schedule_interval_minutes, (int, float)) or schedule_interval_minutes < 1 or schedule_interval_minutes > 1440 ): return { "success": False, "message": "Schedule interval must be between 1 and 1440 minutes", "config_id": None, } if ( not isinstance(stop_after_hours, (int, float)) or stop_after_hours < 1 or stop_after_hours > 168 ): return { "success": False, "message": "Stop after hours must be between 1 and 168 hours (1 week max)", "config_id": None, } # Validate time_to_start if provided if time_to_start: try: parsed_start_time = datetime.fromisoformat( time_to_start.replace("Z", "+00:00") ) if parsed_start_time < datetime.now(): return { "success": False, "message": "Start time cannot be in the past", "config_id": None, } except ValueError: return { "success": False, "message": "Invalid start time format", "config_id": None, } else: parsed_start_time = datetime.now() # Test the API call result = api_client.call_api( method=method, base_url=base_url, endpoint=endpoint, param_keys_values=param_keys_values, header_keys_values=header_keys_values, additional_params=additional_params, ) # Check if the API call failed if isinstance(result, str) and result.startswith("Error"): return { "success": False, "message": f"API call test failed: {result}", "config_id": None, } # Generate config ID and calculate timestamps config_data = { "mcp_api_key": mcp_api_key, "name": name, "description": description, "method": method, "base_url": base_url, "endpoint": endpoint, "param_keys_values": param_keys_values, "header_keys_values": header_keys_values, "additional_params": additional_params, "schedule_interval_minutes": schedule_interval_minutes, "stop_after_hours": stop_after_hours, } # Generate unique config ID config_str = json.dumps(config_data, sort_keys=True) + str( datetime.now().timestamp() ) config_id = int(hashlib.md5(config_str.encode()).hexdigest()[:7], 16) # Calculate timestamps created_at = datetime.now() stop_at = parsed_start_time + timedelta(hours=stop_after_hours) # Add metadata to config config_data.update( { "config_id": config_id, "created_at": created_at.isoformat(), "start_at": parsed_start_time.isoformat(), "stop_at": stop_at.isoformat(), # @JamezyKim This will be used to track the status of whether the api is confirmed or not "is_validated": False, "api_response": result, } ) # Store configuration # TODO: Implement database db_password = os.getenv("DB_PASSWORD") if not db_password: return { "success": False, "message": "Database password not found in environment variables. Please set DB_PASSWORD.", "config_id": None, } conn = psycopg2.connect( database="testdb", user="postgres", host="localhost", password=db_password, port=5432, ) cur = conn.cursor() cur.execute( """ INSERT INTO api_configurations ( config_id, mcp_api_key, name, description, method, base_url, endpoint, params, headers, additional_params, is_validated, is_active, stop, schedule_interval_minutes, time_to_start, created_at, validated_at ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s ) """, ( config_id, mcp_api_key, name, description, method, base_url, endpoint, json.dumps(api_client.parse_key_value_string(param_keys_values)), json.dumps(api_client.parse_key_value_string(header_keys_values)), additional_params, False, False, False, schedule_interval_minutes, parsed_start_time, created_at, None, ), ) conn.commit() cur.execute("SELECT * FROM api_configurations WHERE id = %s", (config_id,)) rows = cur.fetchall() for row in rows: print(row) conn.close() cur.close() # Return success response return { "success": True, "config_id": config_id, "message": f"API call tested and stored successfully for '{name}'. Use this config_id in activate_monitoring() to activate monitoring.", "sample_response": ( json.loads(result) if result.startswith("{") or result.startswith("[") else result ), "start_at": parsed_start_time.isoformat(), "stop_at": stop_at.isoformat(), "schedule_interval_minutes": schedule_interval_minutes, } except Exception as e: return { "success": False, "message": f"Validation failed with error: {str(e)}", "config_id": None, } def activate_monitoring(config_id, mcp_api_key): """ TOOL: Activate periodic monitoring for a validated API configuration. PURPOSE: Start automated recurring API calls based on a previously validated configuration. This is STEP 2 of the monitoring setup process. PREREQUISITE: Must call validate_api_configuration() first and obtain a config_id from successful validation. Make sure that the sample_response is what you expect to see before proceeding with this function. WORKFLOW: 1. First call validate_api_configuration() to get config_id 2. If validation successful, call this function with the config_id 3. Monitoring will run automatically according to the validated schedule Parameters: - config_id: The ID from successful validate_api_configuration() execution (required) - mcp_api_key: User's MCP API key for verification (must match validation step) Input Examples: 1. Activate scheduler for stock monitoring: config_id: 123456789 mcp_api_key: "your_mcp_key_here" 2. Activate scheduler for weather alerts: config_id: 987654321 mcp_api_key: "your_mcp_key_here" NOTE: The config_id must be obtained from a successful validate_api_configuration() response. The mcp_api_key must match the one used during validation. Returns: - Dictionary with success status and scheduling details Example return: { "success": True, "message": "Scheduler activated for 'NVDA Stock Price'", "config_id": 123, "schedule_interval_minutes": 20, "stop_at": "2025-06-11T12:00:00Z", "next_call_at": "2025-06-04T12:20:00Z" } ERROR HANDLING: If config_id not found or invalid, returns success=False with error message """ return { "success": False, "message": "Function not implemented yet; this is a placeholder.", "config_id": config_id, } ## testing if __name__ == "__main__": # Example usage response = validate_api_configuration( mcp_api_key="your_api_key", name="Dog Facts API", description="Monitor random dog facts from a free API", method="GET", base_url="https://dogapi.dog", endpoint="api/v2/facts", param_keys_values="", header_keys_values="", additional_params="{}", schedule_interval_minutes=20, stop_after_hours=24, time_to_start="", ) print(response)