hermes / api_monitor.py
Googolplexic's picture
Update activate_monitoring docstring to clarify prerequisites and emphasize sample_response validation
0359cf2
raw
history blame
13.5 kB
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)