Skip to main content
Glama
aarora79

AWS Cost Explorer MCP Server

get_ec2_spend_last_day

Retrieve EC2 spending data for the previous day using AWS Cost Explorer API to monitor cloud infrastructure costs.

Instructions

Retrieve EC2 spend for the last day using standard AWS Cost Explorer API.

Returns:
    Dict[str, Any]: The raw response from the AWS Cost Explorer API, or None if an error occurs.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paramsYes

Implementation Reference

  • The main handler function for the get_ec2_spend_last_day MCP tool. Decorated with @mcp.tool() which registers it with the FastMCP server. Implements logic to query AWS Cost Explorer for EC2 compute costs grouped by instance type for the last day.
    @mcp.tool()
    async def get_ec2_spend_last_day(params: EC2Params) -> Dict[str, Any]:
        """
        Retrieve EC2 spend for the last day using standard AWS Cost Explorer API.
        
        Returns:
            Dict[str, Any]: The raw response from the AWS Cost Explorer API, or None if an error occurs.
        """
        print(f"get_ec2_spend_last_day, params={params}")
        # Initialize the Cost Explorer client
        ce_client = get_aws_service_boto3_client("ce", params.aws_account_id, params.region)
    
        
        # Calculate the time period - last day
        end_date = datetime.now().strftime('%Y-%m-%d')
        start_date = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
        
        try:
            # Make the API call using get_cost_and_usage (standard API)
            response = ce_client.get_cost_and_usage(
                TimePeriod={
                    'Start': start_date,
                    'End': end_date
                },
                Granularity='DAILY',
                Filter={
                    'Dimensions': {
                        'Key': 'SERVICE',
                        'Values': [
                            'Amazon Elastic Compute Cloud - Compute'
                        ]
                    }
                },
                Metrics=[
                    'UnblendedCost',
                    'UsageQuantity'
                ],
                GroupBy=[
                    {
                        'Type': 'DIMENSION',
                        'Key': 'INSTANCE_TYPE'
                    }
                ]
            )
            
            # Process and print the results
            print(f"EC2 Spend from {start_date} to {end_date}:")
            print("-" * 50)
            
            total_cost = 0.0
            
            if 'ResultsByTime' in response and response['ResultsByTime']:
                time_period_data = response['ResultsByTime'][0]
                
                if 'Groups' in time_period_data:
                    for group in time_period_data['Groups']:
                        instance_type = group['Keys'][0]
                        cost = float(group['Metrics']['UnblendedCost']['Amount'])
                        currency = group['Metrics']['UnblendedCost']['Unit']
                        usage = float(group['Metrics']['UsageQuantity']['Amount'])
                        
                        print(f"Instance Type: {instance_type}")
                        print(f"Cost: {cost:.4f} {currency}")
                        print(f"Usage: {usage:.2f}")
                        print("-" * 30)
                        
                        total_cost += cost
                
                # If no instance-level breakdown, show total
                if not time_period_data.get('Groups'):
                    if 'Total' in time_period_data:
                        total = time_period_data['Total']
                        cost = float(total['UnblendedCost']['Amount'])
                        currency = total['UnblendedCost']['Unit']
                        print(f"Total EC2 Cost: {cost:.4f} {currency}")
                    else:
                        print("No EC2 costs found for this period")
                else:
                    print(f"Total EC2 Cost: {total_cost:.4f} {currency if 'currency' in locals() else 'USD'}")
                    
                # Check if results are estimated
                if 'Estimated' in time_period_data:
                    print(f"Note: These results are {'estimated' if time_period_data['Estimated'] else 'final'}")
            
            return response
            
        except Exception as e:
            print(f"Error retrieving EC2 cost data: {str(e)}")
            return None
  • Pydantic BaseModel schema used as input parameter type for the EC2-related tools, including get_ec2_spend_last_day. Defines validation for days, region, and optional cross-account ID.
    class EC2Params(BaseModel):
        """Parameters for retrieving EC2 Cost Explorer information."""
        days: int = Field(
            default=1,
            description="Number of days to look back for Bedrock logs",
            ge=1,
            le=90
        )
        region: str = Field(
            default="us-east-1",
            description="AWS region to retrieve logs from"
        )
        aws_account_id: Optional[str] = Field(        
            description="AWS account id (if different from the current AWS account) of the account for which to get the cost data",
            default=None
        )
  • Utility function called by the handler to obtain a boto3 Cost Explorer ('ce') client, supporting cross-account access via role assumption.
    def get_aws_service_boto3_client(service: str, aws_account_id: Optional[str], region_name: str, account_b_role_name: Optional[str] = CROSS_ACCOUNT_ROLE_NAME):
        """
        Creates a boto3 client for the specified service in this current AWS account or in a different account
        if an account id is specified.
        
        Args:
            service (str): AWS service name (e.g., 'logs', 'cloudwatch')
            region_name (str): AWS region (e.g. 'us-east-1')
            aws_account_id (str): AWS account ID to access, this is the account in which the role is to be assumed
            account_b_role_name (str): IAM role name to assume
            
        Returns:
            boto3.client: Service client with assumed role credentials
        """
        try:
            this_account = boto3.client('sts').get_caller_identity()['Account']
            if aws_account_id is not None and this_account != aws_account_id:
                # the request is for a different account, we need to assume a role in that account
                print(f"Request is for a different account: {aws_account_id}, current account: {this_account}")
                # Create STS client
                sts_client = boto3.client('sts')
                current_identity = sts_client.get_caller_identity()
                print(f"Current identity: {current_identity}")
                
                # Define the role ARN
                role_arn = f"arn:aws:iam::{aws_account_id}:role/{account_b_role_name}"
                print(f"Attempting to assume role: {role_arn}")
                
                # Assume the role
                assumed_role = sts_client.assume_role(
                    RoleArn=role_arn,
                    RoleSessionName="CrossAccountSession"
                )
                
                # Extract temporary credentials
                credentials = assumed_role['Credentials']
                
                # Create client with assumed role credentials
                client = boto3.client(
                    service,
                    region_name=region_name,
                    aws_access_key_id=credentials['AccessKeyId'],
                    aws_secret_access_key=credentials['SecretAccessKey'],
                    aws_session_token=credentials['SessionToken']
                )
                
                print(f"Successfully created cross-account client for {service} in account {aws_account_id}")
                return client
            else:
                client = boto3.client(
                    service,
                    region_name=region_name
                )
                
                print(f"Successfully created client for {service} in the current AWS account {this_account}")
                return client
            
        except Exception as e:
            print(f"Error creating cross-account client for {service}: {e}")
            raise e
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It mentions the API used and error handling (returns None on error), but lacks details on permissions, rate limits, cost implications, or what 'last day' means precisely (e.g., UTC day, rolling 24h). This is inadequate for a tool that likely requires AWS credentials and has financial implications.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose in the first sentence, followed by a concise return value note. It's appropriately sized with no wasted words, though the return type detail could be more integrated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations, no output schema, and 0% schema description coverage, the description is incomplete. It covers the basic purpose and error handling but misses critical context like authentication needs, cost behavior, and sibling tool differentiation, making it only minimally viable for this AWS cost tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, but it provides no parameter information. The baseline is 3 because the schema fully documents the single 'params' object with its nested fields (days, region, aws_account_id), including defaults and constraints, making the description's lack of param details less critical.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Retrieve' and resource 'EC2 spend for the last day', specifying the AWS Cost Explorer API as the method. It distinguishes from siblings by focusing on EC2 spend rather than Bedrock usage or detailed breakdowns, though it doesn't explicitly contrast them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives is provided. The description doesn't mention sibling tools or suggest scenarios for choosing this tool over others, leaving the agent without usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/aarora79/aws-cost-explorer-mcp-server'

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