awslabs.cost-explorer-mcp-server 0.0.4__py3-none-any.whl → 0.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,88 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Cost Explorer MCP server implementation.
16
+
17
+ Metadata tools for Cost Explorer MCP Server.
18
+ """
19
+
20
+ import os
21
+ import sys
22
+ from awslabs.cost_explorer_mcp_server.helpers import (
23
+ get_available_dimension_values,
24
+ get_available_tag_values,
25
+ )
26
+ from awslabs.cost_explorer_mcp_server.models import DateRange, DimensionKey
27
+ from loguru import logger
28
+ from mcp.server.fastmcp import Context
29
+ from pydantic import Field
30
+ from typing import Any, Dict
31
+
32
+
33
+ # Configure Loguru logging
34
+ logger.remove()
35
+ logger.add(sys.stderr, level=os.getenv('FASTMCP_LOG_LEVEL', 'WARNING'))
36
+
37
+
38
+ async def get_dimension_values(
39
+ ctx: Context, date_range: DateRange, dimension: DimensionKey
40
+ ) -> Dict[str, Any]:
41
+ """Retrieve available dimension values for AWS Cost Explorer.
42
+
43
+ This tool retrieves all available and valid values for a specified dimension (e.g., SERVICE, REGION)
44
+ over a period of time. This is useful for validating filter values or exploring available options
45
+ for cost analysis.
46
+
47
+ Args:
48
+ ctx: MCP context
49
+ date_range: The billing period start and end dates in YYYY-MM-DD format
50
+ dimension: The dimension key to retrieve values for (e.g., SERVICE, REGION, LINKED_ACCOUNT)
51
+
52
+ Returns:
53
+ Dictionary containing the dimension name and list of available values
54
+ """
55
+ try:
56
+ response = get_available_dimension_values(
57
+ dimension.dimension_key, date_range.start_date, date_range.end_date
58
+ )
59
+ return response
60
+ except Exception as e:
61
+ logger.error(f'Error getting dimension values for {dimension.dimension_key}: {e}')
62
+ return {'error': f'Error getting dimension values: {str(e)}'}
63
+
64
+
65
+ async def get_tag_values(
66
+ ctx: Context,
67
+ date_range: DateRange,
68
+ tag_key: str = Field(..., description='The tag key to retrieve values for'),
69
+ ) -> Dict[str, Any]:
70
+ """Retrieve available tag values for AWS Cost Explorer.
71
+
72
+ This tool retrieves all available values for a specified tag key over a period of time.
73
+ This is useful for validating tag filter values or exploring available tag options for cost analysis.
74
+
75
+ Args:
76
+ ctx: MCP context
77
+ date_range: The billing period start and end dates in YYYY-MM-DD format
78
+ tag_key: The tag key to retrieve values for
79
+
80
+ Returns:
81
+ Dictionary containing the tag key and list of available values
82
+ """
83
+ try:
84
+ response = get_available_tag_values(tag_key, date_range.start_date, date_range.end_date)
85
+ return response
86
+ except Exception as e:
87
+ logger.error(f'Error getting tag values for {tag_key}: {e}')
88
+ return {'error': f'Error getting tag values: {str(e)}'}
@@ -0,0 +1,70 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from awslabs.cost_explorer_mcp_server.constants import VALID_DIMENSIONS
16
+ from awslabs.cost_explorer_mcp_server.helpers import validate_date_format, validate_date_range
17
+ from pydantic import BaseModel, Field, field_validator
18
+
19
+
20
+ """Data models and validation logic for Cost Explorer MCP Server.
21
+ """
22
+
23
+
24
+ class DateRange(BaseModel):
25
+ """Date range model for cost queries."""
26
+
27
+ start_date: str = Field(
28
+ description='The start date of the billing period in YYYY-MM-DD format. Defaults to last month, if not provided.'
29
+ )
30
+ end_date: str = Field(description='The end date of the billing period in YYYY-MM-DD format.')
31
+
32
+ @field_validator('start_date', 'end_date')
33
+ @classmethod
34
+ def validate_individual_dates(cls, v):
35
+ """Validate that individual dates are in YYYY-MM-DD format and are valid dates."""
36
+ is_valid, error = validate_date_format(v)
37
+ if not is_valid:
38
+ raise ValueError(error)
39
+ return v
40
+
41
+ def model_post_init(self, __context):
42
+ """Validate the date range after both dates are set."""
43
+ is_valid, error = validate_date_range(self.start_date, self.end_date)
44
+ if not is_valid:
45
+ raise ValueError(error)
46
+
47
+ def validate_with_granularity(self, granularity: str):
48
+ """Validate the date range with granularity-specific constraints."""
49
+ is_valid, error = validate_date_range(self.start_date, self.end_date, granularity)
50
+ if not is_valid:
51
+ raise ValueError(error)
52
+
53
+
54
+ class DimensionKey(BaseModel):
55
+ """Dimension key model."""
56
+
57
+ dimension_key: str = Field(
58
+ description=f'The name of the dimension to retrieve values for. Valid values are {", ".join(VALID_DIMENSIONS)}.'
59
+ )
60
+
61
+ @field_validator('dimension_key')
62
+ @classmethod
63
+ def validate_dimension_key(cls, v):
64
+ """Validate that the dimension key is supported by AWS Cost Explorer."""
65
+ dimension_upper = v.upper()
66
+ if dimension_upper not in VALID_DIMENSIONS:
67
+ raise ValueError(
68
+ f"Invalid dimension key '{v}'. Valid dimensions are: {', '.join(VALID_DIMENSIONS)}"
69
+ )
70
+ return v