Skip to main content
Glama
tools.py13.9 kB
"""AWS FIS FastMCP Server tools implementation.""" import json import uuid from datetime import datetime from typing import Dict, List, Any, Optional from functools import wraps import boto3 # Global variable to track write mode _WRITE_MODE_ENABLED = False def set_write_mode(enabled: bool) -> None: """Set the global write mode.""" global _WRITE_MODE_ENABLED _WRITE_MODE_ENABLED = enabled def require_write_mode(func): """Decorator to require write mode for destructive operations.""" @wraps(func) def wrapper(*args, **kwargs): if not _WRITE_MODE_ENABLED: return json.dumps({ "error": "Write operations are disabled", "message": f"The '{func.__name__}' operation requires write mode. Please restart the server with --allow-writes flag to enable write operations.", "operation": func.__name__, "read_only_mode": True }, indent=2) return func(*args, **kwargs) return wrapper def _serialize_datetime(obj: Any) -> Any: """ Recursively serialize datetime objects to ISO format strings. Args: obj: Object that may contain datetime objects Returns: Object with datetime objects converted to ISO format strings """ if isinstance(obj, datetime): return obj.isoformat() elif isinstance(obj, dict): return {key: _serialize_datetime(value) for key, value in obj.items()} elif isinstance(obj, list): return [_serialize_datetime(item) for item in obj] else: return obj def list_experiment_templates(region: str = "us-east-1") -> str: """ List all AWS FIS experiment templates in the specified region. Args: region: AWS region to query (default: us-east-1) Returns: JSON string containing experiment templates information """ try: fis = boto3.client('fis', region_name=region) response = fis.list_experiment_templates() if not response.get('experimentTemplates'): return "No experiment templates found in region " + region # Format the response for better readability templates = [] for template in response['experimentTemplates']: templates.append({ 'id': template.get('id'), 'name': template.get('experimentTemplateId'), 'description': template.get('description'), 'creationTime': template.get('creationTime').isoformat() if template.get('creationTime') else None, 'lastUpdateTime': template.get('lastUpdateTime').isoformat() if template.get('lastUpdateTime') else None, 'tags': template.get('tags', {}) }) return json.dumps(templates, indent=2) except Exception as e: return f"Error listing experiment templates: {str(e)}" def get_experiment_template(template_id: str, region: str = "us-east-1") -> str: """ Get detailed information about a specific AWS FIS experiment template. Args: template_id: ID of the experiment template to retrieve region: AWS region to query (default: us-east-1) Returns: JSON string containing detailed template information """ try: fis = boto3.client('fis', region_name=region) response = fis.get_experiment_template(id=template_id) # Format the response for better readability template = response.get('experimentTemplate', {}) formatted_template = { 'id': template.get('id'), 'description': template.get('description'), 'targets': template.get('targets', {}), 'actions': template.get('actions', {}), 'stopConditions': template.get('stopConditions', []), 'roleArn': template.get('roleArn'), 'tags': template.get('tags', {}) } return json.dumps(formatted_template, indent=2) except Exception as e: return f"Error retrieving experiment template: {str(e)}" def list_experiments(region: str = "us-east-1") -> str: """ List all AWS FIS experiments in the specified region. Args: region: AWS region to query (default: us-east-1) Returns: JSON string containing experiments information """ try: fis = boto3.client('fis', region_name=region) response = fis.list_experiments() if not response.get('experiments'): return "No experiments found in region " + region # Format the response for better readability experiments = [] for experiment in response['experiments']: experiments.append({ 'id': experiment.get('id'), 'experimentTemplateId': experiment.get('experimentTemplateId'), 'state': experiment.get('state', {}).get('status'), 'startTime': experiment.get('startTime').isoformat() if experiment.get('startTime') else None, 'endTime': experiment.get('endTime').isoformat() if experiment.get('endTime') else None, 'tags': experiment.get('tags', {}) }) return json.dumps(experiments, indent=2) except Exception as e: return f"Error listing experiments: {str(e)}" def get_experiment(experiment_id: str, region: str = "us-east-1") -> str: """ Get detailed information about a specific AWS FIS experiment. Args: experiment_id: ID of the experiment to retrieve region: AWS region to query (default: us-east-1) Returns: JSON string containing detailed experiment information """ try: fis = boto3.client('fis', region_name=region) response = fis.get_experiment(id=experiment_id) # Get the raw experiment data experiment = response.get('experiment', {}) # Use the recursive datetime serializer to handle all datetime objects serialized_experiment = _serialize_datetime(experiment) return json.dumps(serialized_experiment, indent=2) except Exception as e: return f"Error retrieving experiment: {str(e)}" @require_write_mode def start_experiment(template_id: str, region: str = "us-east-1", client_token: Optional[str] = None) -> str: """ Start a new AWS FIS experiment based on an experiment template. Args: template_id: ID of the experiment template to use region: AWS region to use (default: us-east-1) client_token: Optional client token for idempotency Returns: JSON string containing the started experiment information """ try: fis = boto3.client('fis', region_name=region) # Generate a client token if not provided if not client_token: client_token = str(uuid.uuid4()) response = fis.start_experiment( experimentTemplateId=template_id, clientToken=client_token ) # Get the raw experiment data and serialize datetime objects experiment = response.get('experiment', {}) serialized_experiment = _serialize_datetime(experiment) return json.dumps(serialized_experiment, indent=2) except Exception as e: return f"Error starting experiment: {str(e)}" @require_write_mode def stop_experiment(experiment_id: str, region: str = "us-east-1") -> str: """ Stop a running AWS FIS experiment. Args: experiment_id: ID of the experiment to stop region: AWS region to use (default: us-east-1) Returns: JSON string containing the stopped experiment information """ try: fis = boto3.client('fis', region_name=region) response = fis.stop_experiment(id=experiment_id) # Get the raw experiment data and serialize datetime objects experiment = response.get('experiment', {}) serialized_experiment = _serialize_datetime(experiment) return json.dumps(serialized_experiment, indent=2) except Exception as e: return f"Error stopping experiment: {str(e)}" @require_write_mode def create_experiment_template( name: str, description: str, targets: Dict[str, Dict[str, Any]], actions: Dict[str, Dict[str, Any]], role_arn: str, stop_conditions: List[Dict[str, Any]], region: str = "us-east-1" ) -> str: """ Create a new AWS FIS experiment template. Args: name: Name for the experiment template description: Description of the experiment template targets: Dictionary of targets configuration actions: Dictionary of actions configuration role_arn: ARN of the IAM role to use for the experiment stop_conditions: List of stop conditions region: AWS region to use (default: us-east-1) Returns: JSON string containing the created template information """ try: fis = boto3.client('fis', region_name=region) response = fis.create_experiment_template( clientToken=str(uuid.uuid4()), description=description, targets=targets, actions=actions, stopConditions=stop_conditions, roleArn=role_arn, tags={'Name': name} ) # Format the response for better readability template = response.get('experimentTemplate', {}) formatted_template = { 'id': template.get('id'), 'description': template.get('description'), 'creationTime': template.get('creationTime').isoformat() if template.get('creationTime') else None, 'tags': template.get('tags', {}) } return json.dumps(formatted_template, indent=2) except Exception as e: return f"Error creating experiment template: {str(e)}" @require_write_mode def delete_experiment_template(template_id: str, region: str = "us-east-1") -> str: """ Delete an AWS FIS experiment template. Args: template_id: ID of the experiment template to delete region: AWS region to use (default: us-east-1) Returns: Success or error message """ try: fis = boto3.client('fis', region_name=region) fis.delete_experiment_template(id=template_id) return f"Successfully deleted experiment template {template_id}" except Exception as e: return f"Error deleting experiment template: {str(e)}" def list_action_types(region: str = "us-east-1") -> str: """ List all available AWS FIS action types. Args: region: AWS region to query (default: us-east-1) Returns: JSON string containing action types information """ try: fis = boto3.client('fis', region_name=region) response = fis.list_actions() # Fixed: Changed from list_action_types() to list_actions() if not response.get('actions'): # Fixed: Changed from 'actionTypes' to 'actions' return "No actions found in region " + region # Format the response for better readability actions = [] for action in response['actions']: # Fixed: Changed from 'actionTypes' to 'actions' actions.append({ 'id': action.get('id'), 'description': action.get('description'), 'targets': action.get('targets', {}), 'parameters': action.get('parameters', {}) }) return json.dumps(actions, indent=2) except Exception as e: return f"Error listing actions: {str(e)}" def generate_template_example( target_type: str = "aws:ec2:instance", action_type: str = "aws:ec2:stop-instances", region: str = "us-east-1" ) -> str: """ Generate an example AWS FIS experiment template for a given target and action type. Args: target_type: Target resource type (default: aws:ec2:instance) action_type: Action type to perform (default: aws:ec2:stop-instances) region: AWS region to use (default: us-east-1) Returns: JSON string containing an example template configuration """ try: # Example template structure template = { "description": f"Example experiment template for {action_type} on {target_type}", "targets": { "MyTargets": { "resourceType": target_type, "resourceArns": ["REPLACE_WITH_ACTUAL_RESOURCE_ARN"], "selectionMode": "ALL" } }, "actions": { "MyAction": { "actionId": action_type, "parameters": {}, "targets": { "Instances": "MyTargets" } } }, "stopConditions": [ { "source": "none" } ], "roleArn": "REPLACE_WITH_ACTUAL_ROLE_ARN", "tags": { "Name": f"Example-{target_type}-{action_type}" } } # Add common parameters based on action type if action_type == "aws:ec2:stop-instances": template["actions"]["MyAction"]["parameters"] = { "startAfter": "PT0M" } elif action_type == "aws:ec2:reboot-instances": template["actions"]["MyAction"]["parameters"] = { "startAfter": "PT0M" } return json.dumps(template, indent=2) except Exception as e: return f"Error generating template example: {str(e)}"

Implementation Reference

Latest Blog Posts

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/RadiumGu/aws-fis-mcp-server'

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