From 67aec7555c1b6b9f3256e7b331dc1599b4a6162a Mon Sep 17 00:00:00 2001 From: "wenfeng.wf" Date: Fri, 25 Apr 2025 18:18:19 +0800 Subject: [PATCH 1/6] Fix perf_key list --- .../server.py | 16 ++++++++++------ src/alibabacloud_rds_openapi_mcp_server/utils.py | 15 ++++++++++++--- uv.lock | 2 +- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/alibabacloud_rds_openapi_mcp_server/server.py b/src/alibabacloud_rds_openapi_mcp_server/server.py index f96c9e2..679b547 100644 --- a/src/alibabacloud_rds_openapi_mcp_server/server.py +++ b/src/alibabacloud_rds_openapi_mcp_server/server.py @@ -13,7 +13,7 @@ from mcp.server.fastmcp import FastMCP current_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.append(current_dir) -from utils import transform_to_iso_8601, transform_to_datetime, transform_perf_key +from utils import transform_to_iso_8601, transform_to_datetime, transform_perf_key, compress_json_array logger = logging.getLogger(__name__) @@ -94,15 +94,19 @@ async def describe_db_instance_attribute(region_id: str, db_instance_id: str): @mcp.tool() -async def describe_db_instance_performance(region_id: str, db_instance_id: str, db_type: str, perf_key: str, - start_time: str, end_time: str): +async def describe_db_instance_performance(region_id: str, + db_instance_id: str, + db_type: str, + perf_keys: list[str], + start_time: str, + end_time: str): """ Queries the performance data of an instance. Args: region_id: db instance region(e.g. cn-hangzhou) db_instance_id: db instance id(e.g. rm-xxx) db_type: the db instance database type(e.g. mysql,pgsql,sqlserver) - perf_key: Performance Key(e.g. MemCpuUsage,QPSTPS,Sessions,COMDML,RowDML) + perf_keys: Performance Key (e.g. ["MemCpuUsage", "QPSTPS", "Sessions", "COMDML", "RowDML"]) start_time: start time(e.g. 2023-01-01 00:00) end_time: end time(e.g. 2023-01-01 00:00) """ @@ -110,7 +114,7 @@ async def describe_db_instance_performance(region_id: str, db_instance_id: str, start_time = transform_to_datetime(start_time) end_time = transform_to_datetime(end_time) client = get_rds_client(region_id) - perf_key = transform_perf_key(db_type, perf_key) + perf_key = transform_perf_key(db_type, perf_keys) if not perf_key: raise OpenAPIError(f"Unsupported perf_key: {perf_key}") request = rds_20140815_models.DescribeDBInstancePerformanceRequest( @@ -122,7 +126,7 @@ async def describe_db_instance_performance(region_id: str, db_instance_id: str, response = client.describe_dbinstance_performance(request) responses = [] for perf_key in response.body.performance_keys.performance_key: - perf_key_info = f"""Key={perf_key.key}; Unit={perf_key.unit}; ValueFormat={perf_key.value_format}; Values={"|".join([f"{value.date} {value.value}" for value in perf_key.values.performance_value])}""" + perf_key_info = f"""Key={perf_key.key}; Unit={perf_key.unit}; ValueFormat={perf_key.value_format}; Values={compress_json_array([item.to_map() for item in perf_key.values.performance_value])}""" responses.append(perf_key_info) return responses except Exception as e: diff --git a/src/alibabacloud_rds_openapi_mcp_server/utils.py b/src/alibabacloud_rds_openapi_mcp_server/utils.py index fbbb93e..db5c449 100644 --- a/src/alibabacloud_rds_openapi_mcp_server/utils.py +++ b/src/alibabacloud_rds_openapi_mcp_server/utils.py @@ -41,11 +41,20 @@ def transform_to_datetime(s: str): return dt -def transform_perf_key(db_type: str, perf_key: str): +def transform_perf_key(db_type: str, perf_keys: list[str]): perf_key_after_transform = [] - for key in perf_key.split(","): + for key in perf_keys: if key in PERF_KEYS[db_type.lower()]: perf_key_after_transform.extend(PERF_KEYS[db_type.lower()][key]) else: perf_key_after_transform.append(key) - return PERF_KEYS[db_type.lower()][perf_key] \ No newline at end of file + return perf_key_after_transform + + +def compress_json_array(json_array: list[dict]): + if not json_array or len(json_array) == 0: + return "" + compress_str = ";".join(json_array[0].keys()) + for item in json_array: + compress_str += "|" + ";".join([str(item[key]) for key in json_array[0].keys()]) + return compress_str diff --git a/uv.lock b/uv.lock index e44b487..f9670bb 100644 --- a/uv.lock +++ b/uv.lock @@ -111,7 +111,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/f6/50/5f41ab550d7874c62 [[package]] name = "alibabacloud-rds-openapi-mcp-server" -version = "1.2.1" +version = "1.2.2" source = { virtual = "." } dependencies = [ { name = "alibabacloud-rds20140815" }, From b3190a57236dc713869cf6d9eb8b4d671669b320 Mon Sep 17 00:00:00 2001 From: "wenfeng.wf" Date: Fri, 25 Apr 2025 18:21:48 +0800 Subject: [PATCH 2/6] clear some params. --- src/alibabacloud_rds_openapi_mcp_server/server.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/alibabacloud_rds_openapi_mcp_server/server.py b/src/alibabacloud_rds_openapi_mcp_server/server.py index 679b547..63f75db 100644 --- a/src/alibabacloud_rds_openapi_mcp_server/server.py +++ b/src/alibabacloud_rds_openapi_mcp_server/server.py @@ -379,7 +379,6 @@ async def create_db_instance( private_ip_address: str = None, client_token: str = None, resource_group_id: str = None, - dedicated_host_group_id: str = None, tde_status: str = None, encryption_key: str = None, serverless_config: Dict[str, Any] = None @@ -406,7 +405,6 @@ async def create_db_instance( private_ip_address (str, optional): Private IP address. client_token (str, optional): Idempotence token. resource_group_id (str, optional): Resource group ID. - dedicated_host_group_id (str, optional): Dedicated host group ID. tde_status (str, optional): TDE status (Enable, Disable). encryption_key (str, optional): Custom encryption key. serverless_config (Dict[str, Any], optional): Serverless instance configuration. @@ -452,8 +450,6 @@ async def create_db_instance( request.client_token = client_token if resource_group_id: request.resource_group_id = resource_group_id - if dedicated_host_group_id: - request.dedicated_host_group_id = dedicated_host_group_id if tde_status: request.tde_status = tde_status if encryption_key: @@ -621,8 +617,6 @@ async def describe_vswitches( resource_group_id: str = None, page_number: int = 1, page_size: int = 10, - vswitch_owner_id: int = None, - tags: List[Dict[str, str]] = None ) -> Dict[str, Any]: """Query VSwitch list. @@ -635,8 +629,6 @@ async def describe_vswitches( resource_group_id (str, optional): The resource group ID of the VSwitch. page_number (int, optional): The page number of the list. Default: 1. page_size (int, optional): The number of entries per page. Maximum value: 50. Default: 10. - vswitch_owner_id (int, optional): The Alibaba Cloud account ID of the VSwitch owner. - tags (List[Dict[str, str]], optional): The tags of the resource. Returns: Dict[str, Any]: The response containing the list of VSwitches. @@ -667,10 +659,6 @@ async def describe_vswitches( request.is_default = is_default if resource_group_id: request.resource_group_id = resource_group_id - if vswitch_owner_id: - request.vswitch_owner_id = vswitch_owner_id - if tags: - request.tag = tags # Make the API request response = client.describe_vswitches(request) From 1d009b8c8eadd97e3ed76ca84181ed366ce169ba Mon Sep 17 00:00:00 2001 From: "wenfeng.wf" Date: Fri, 25 Apr 2025 18:40:56 +0800 Subject: [PATCH 3/6] clear some params. --- .../server.py | 214 +++++++++--------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/src/alibabacloud_rds_openapi_mcp_server/server.py b/src/alibabacloud_rds_openapi_mcp_server/server.py index 63f75db..1a0a6cd 100644 --- a/src/alibabacloud_rds_openapi_mcp_server/server.py +++ b/src/alibabacloud_rds_openapi_mcp_server/server.py @@ -37,7 +37,7 @@ def get_vpc_client(region_id: str) -> VpcClient: """Get VPC client instance. Args: - region_id (str): The region ID for the VPC client. + region_id: The region ID for the VPC client. Returns: VpcClient: The VPC client instance for the specified region. @@ -147,15 +147,15 @@ async def modify_parameter( """Modify RDS instance parameters. Args: - region_id (str): The region ID of the RDS instance. - dbinstance_id (str): The ID of the RDS instance. + region_id: The region ID of the RDS instance. + dbinstance_id: The ID of the RDS instance. parameters (Dict[str, str], optional): Parameters and their values in JSON format. Example: {"delayed_insert_timeout": "600", "max_length_for_sort_data": "2048"} - parameter_group_id (str, optional): Parameter template ID. - forcerestart (bool, optional): Whether to force restart the database. Default: False. - switch_time_mode (str, optional): Execution time mode. Values: Immediate, MaintainTime, ScheduleTime. Default: Immediate. - switch_time (str, optional): Scheduled execution time in format: yyyy-MM-ddTHH:mm:ssZ (UTC time). - client_token (str, optional): Client token for idempotency, max 64 ASCII characters. + parameter_group_id: Parameter template ID. + forcerestart: Whether to force restart the database. Default: False. + switch_time_mode: Execution time mode. Values: Immediate, MaintainTime, ScheduleTime. Default: Immediate. + switch_time: Scheduled execution time in format: yyyy-MM-ddTHH:mm:ssZ (UTC time). + client_token: Client token for idempotency, max 64 ASCII characters. Returns: Dict[str, Any]: The response containing the request ID. @@ -216,26 +216,26 @@ async def modify_db_instance_spec( """Modify RDS instance specifications. Args: - region_id (str): The region ID of the RDS instance. - dbinstance_id (str): The ID of the RDS instance. - dbinstance_class (str, optional): Target instance specification. - dbinstance_storage (int, optional): Target storage space in GB. - pay_type (str, optional): Instance payment type. Values: Postpaid, Prepaid, Serverless. - effective_time (str, optional): When the new configuration takes effect. Values: Immediate, MaintainTime, ScheduleTime. - switch_time (str, optional): Scheduled switch time in format: yyyy-MM-ddTHH:mm:ssZ (UTC time). - switch_time_mode (str, optional): Switch time mode. Values: Immediate, MaintainTime, ScheduleTime. - source_biz (str, optional): Source business type. - dedicated_host_group_id (str, optional): Dedicated host group ID. - zone_id (str, optional): Zone ID. - vswitch_id (str, optional): VSwitch ID. - category (str, optional): Instance category. - instance_network_type (str, optional): Instance network type. - direction (str, optional): Specification change direction. Values: UP, DOWN. - auto_pause (bool, optional): Whether to enable auto pause for Serverless instances. - max_capacity (float, optional): Maximum capacity for Serverless instances. - min_capacity (float, optional): Minimum capacity for Serverless instances. - switch_force (bool, optional): Whether to force switch for Serverless instances. - client_token (str, optional): Client token for idempotency, max 64 ASCII characters. + region_id: The region ID of the RDS instance. + dbinstance_id: The ID of the RDS instance. + dbinstance_class: Target instance specification. + dbinstance_storage: Target storage space in GB. + pay_type: Instance payment type. Values: Postpaid, Prepaid, Serverless. + effective_time: When the new configuration takes effect. Values: Immediate, MaintainTime, ScheduleTime. + switch_time: Scheduled switch time in format: yyyy-MM-ddTHH:mm:ssZ (UTC time). + switch_time_mode: Switch time mode. Values: Immediate, MaintainTime, ScheduleTime. + source_biz: Source business type. + dedicated_host_group_id: Dedicated host group ID. + zone_id: Zone ID. + vswitch_id: VSwitch ID. + category: Instance category. + instance_network_type: Instance network type. + direction: Specification change direction. Values: UP, DOWN. + auto_pause: Whether to enable auto pause for Serverless instances. + max_capacity: Maximum capacity for Serverless instances. + min_capacity: Minimum capacity for Serverless instances. + switch_force: Whether to force switch for Serverless instances. + client_token: Client token for idempotency, max 64 ASCII characters. Returns: Dict[str, Any]: The response containing the request ID. @@ -312,16 +312,16 @@ async def describe_available_classes( """Query the RDS instance class_code and storage space that can be purchased in the inventory. Args: - region_id (str): The region ID of the RDS instance. - zone_id (str): The zone ID of the RDS instance. Query available zones by `describe_available_zones`. - instance_charge_type (str): Instance payment type. Values: Prepaid, Postpaid, Serverless. - engine (str): Database engine type. Values: MySQL, SQLServer, PostgreSQL, MariaDB. - engine_version (str): Database version. - dbinstance_storage_type (str): Storage type. Values: local_ssd,general_essd,cloud_essd,cloud_essd2,cloud_essd3 - category (str): Instance category. Values: Basic, HighAvailability, cluster, AlwaysOn, Finance, serverless_basic, serverless_standard, serverless_ha. - dbinstance_id (str, optional): The ID of the RDS instance. - order_type (str, optional): Order type. Currently only supports "BUY". - commodity_code (str, optional): Commodity code for read-only instances. + region_id: The region ID of the RDS instance. + zone_id: The zone ID of the RDS instance. Query available zones by `describe_available_zones`. + instance_charge_type: Instance payment type. Values: Prepaid, Postpaid, Serverless. + engine: Database engine type. Values: MySQL, SQLServer, PostgreSQL, MariaDB. + engine_version: Database version. + dbinstance_storage_type: Storage type. Values: local_ssd,general_essd,cloud_essd,cloud_essd2,cloud_essd3 + category: Instance category. Values: Basic, HighAvailability, cluster, AlwaysOn, Finance, serverless_basic, serverless_standard, serverless_ha. + dbinstance_id: The ID of the RDS instance. + order_type: Order type. Currently only supports "BUY". + commodity_code: Commodity code for read-only instances. Returns: Dict[str, Any]: The response containing available instance classes and storage ranges. @@ -386,28 +386,28 @@ async def create_db_instance( """Create an RDS instance. Args: - region_id (str): Region ID. - engine (str): Database type (MySQL, SQLServer, PostgreSQL, MariaDB). - engine_version (str): Database version. - dbinstance_class (str): Instance specification. Query available class_codes by `describe_available_classes`. - dbinstance_storage (int): Storage space in GB. - security_ip_list (str): IP whitelist, separated by commas. Default: "127.0.0.1". - instance_network_type (str, optional): Network type (Classic, VPC). Default: VPC. - zone_id (str, optional): Zone ID. Query available zones by `describe_available_zones`. - pay_type (str, optional): Payment type (Postpaid, Prepaid). Default: Postpaid. - instance_charge_type (str, optional): Instance charge type. - system_db_charset (str, optional): Character set. - dbinstance_net_type (str, optional): Network connection type (Internet, Intranet). Default: Internet. - category (str, optional): Instance category. Default: Basic. - dbinstance_storage_type (str, optional): Storage type. (e.g. local_ssd,general_essd,cloud_essd,cloud_essd2,cloud_essd3) - vpc_id (str): VPC ID. - vswitch_id (str): VSwitch ID. - private_ip_address (str, optional): Private IP address. - client_token (str, optional): Idempotence token. - resource_group_id (str, optional): Resource group ID. - tde_status (str, optional): TDE status (Enable, Disable). - encryption_key (str, optional): Custom encryption key. - serverless_config (Dict[str, Any], optional): Serverless instance configuration. + region_id: Region ID. + engine: Database type (MySQL, SQLServer, PostgreSQL, MariaDB). + engine_version: Database version. + dbinstance_class: Instance specification. Query available class_codes by `describe_available_classes`. + dbinstance_storage: Storage space in GB. + security_ip_list: IP whitelist, separated by commas. Default: "127.0.0.1". + instance_network_type: Network type (Classic, VPC). Default: VPC. + zone_id: Zone ID. Query available zones by `describe_available_zones`. + pay_type: Payment type (Postpaid, Prepaid). Default: Postpaid. + instance_charge_type: Instance charge type. + system_db_charset: Character set. + dbinstance_net_type: Network connection type (Internet, Intranet). Default: Internet. + category: Instance category. Default: Basic. + dbinstance_storage_type: Storage type. (e.g. local_ssd,general_essd,cloud_essd,cloud_essd2,cloud_essd3) + vpc_id: VPC ID. + vswitch_id: VSwitch ID. + private_ip_address: Private IP address. + client_token: Idempotence token. + resource_group_id: Resource group ID. + tde_status: TDE status (Enable, Disable). + encryption_key: Custom encryption key. + serverless_config: Serverless instance configuration. Returns: Dict[str, Any]: Response containing the created instance details. @@ -479,14 +479,14 @@ async def describe_available_zones( """Query available zones for RDS instances. Args: - region_id (str): Region ID. - engine (str): Database type (MySQL, SQLServer, PostgreSQL, MariaDB). - engine_version (str, optional): Database version. + region_id: Region ID. + engine: Database type (MySQL, SQLServer, PostgreSQL, MariaDB). + engine_version: Database version. MySQL: 5.5, 5.6, 5.7, 8.0 SQL Server: 2008r2, 2012, 2014, 2016, 2017, 2019 PostgreSQL: 10.0, 11.0, 12.0, 13.0, 14.0, 15.0 MariaDB: 10.3 - commodity_code (str, optional): Commodity code. + commodity_code: Commodity code. bards: Pay-as-you-go primary instance (China site) rds: Subscription primary instance (China site) rords: Pay-as-you-go read-only instance (China site) @@ -497,12 +497,12 @@ async def describe_available_zones( rds_rordspre_public_intl: Subscription read-only instance (International site) rds_serverless_public_cn: Serverless instance (China site) rds_serverless_public_intl: Serverless instance (International site) - zone_id (str, optional): Zone ID. - dispense_mode (str, optional): Whether to return zones that support single-zone deployment. + zone_id: Zone ID. + dispense_mode: Whether to return zones that support single-zone deployment. 1: Return (default) 0: Do not return - dbinstance_name (str, optional): Primary instance ID. Required when querying read-only instance resources. - category (str, optional): Instance category. + dbinstance_name: Primary instance ID. Required when querying read-only instance resources. + category: Instance category. Basic: Basic Edition HighAvailability: High-availability Edition cluster: MySQL Cluster Edition @@ -562,14 +562,14 @@ async def describe_vpcs( """Query VPC list. Args: - region_id (str): The region ID of the VPC. - vpc_id (str, optional): The ID of the VPC. Up to 20 VPC IDs can be specified, separated by commas. - vpc_name (str, optional): The name of the VPC. - resource_group_id (str, optional): The resource group ID of the VPC to query. - page_number (int, optional): The page number of the list. Default: 1. - page_size (int, optional): The number of entries per page. Maximum value: 50. Default: 10. - vpc_owner_id (int, optional): The Alibaba Cloud account ID of the VPC owner. - tags (List[Dict[str, str]], optional): The tags of the resource. + region_id: The region ID of the VPC. + vpc_id: The ID of the VPC. Up to 20 VPC IDs can be specified, separated by commas. + vpc_name: The name of the VPC. + resource_group_id: The resource group ID of the VPC to query. + page_number: The page number of the list. Default: 1. + page_size: The number of entries per page. Maximum value: 50. Default: 10. + vpc_owner_id: The Alibaba Cloud account ID of the VPC owner. + tags: The tags of the resource. Returns: Dict[str, Any]: The response containing the list of VPCs. @@ -621,14 +621,14 @@ async def describe_vswitches( """Query VSwitch list. Args: - region_id (str, optional): The region ID of the VSwitch. At least one of region_id or vpc_id must be specified. - vpc_id (str, optional): The ID of the VPC to which the VSwitch belongs. At least one of region_id or vpc_id must be specified. - vswitch_id (str, optional): The ID of the VSwitch to query. - zone_id (str, optional): The zone ID of the VSwitch. - vswitch_name (str, optional): The name of the VSwitch. - resource_group_id (str, optional): The resource group ID of the VSwitch. - page_number (int, optional): The page number of the list. Default: 1. - page_size (int, optional): The number of entries per page. Maximum value: 50. Default: 10. + region_id: The region ID of the VSwitch. At least one of region_id or vpc_id must be specified. + vpc_id: The ID of the VPC to which the VSwitch belongs. At least one of region_id or vpc_id must be specified. + vswitch_id: The ID of the VSwitch to query. + zone_id: The zone ID of the VSwitch. + vswitch_name: The name of the VSwitch. + resource_group_id: The resource group ID of the VSwitch. + page_number: The page number of the list. Default: 1. + page_size: The number of entries per page. Maximum value: 50. Default: 10. Returns: Dict[str, Any]: The response containing the list of VSwitches. @@ -684,18 +684,18 @@ async def describe_slow_log_records( """Query slow log records for an RDS instance. Args: - region_id (str): The region ID of the RDS instance. - dbinstance_id (str): The ID of the RDS instance. - start_time (str): Start time in format: yyyy-MM-dd HH:mm. + region_id: The region ID of the RDS instance. + dbinstance_id: The ID of the RDS instance. + start_time: Start time in format: yyyy-MM-dd HH:mm. Cannot be earlier than 30 days before the current time. - end_time (str): End time in format: yyyy-MM-dd HH:mm. + end_time: End time in format: yyyy-MM-dd HH:mm. Must be later than the start time. - sqlhash (str, optional): The unique identifier of the SQL statement in slow log statistics. + sqlhash: The unique identifier of the SQL statement in slow log statistics. Used to get slow log details for a specific SQL statement. - db_name (str, optional): The name of the database. - page_size (int, optional): Number of records per page. Range: 30-100. Default: 30. - page_number (int, optional): Page number. Must be greater than 0 and not exceed Integer max value. Default: 1. - node_id (str, optional): Node ID. Only applicable to cluster instances. + db_name: The name of the database. + page_size: Number of records per page. Range: 30-100. Default: 30. + page_number: Page number. Must be greater than 0 and not exceed Integer max value. Default: 1. + node_id: Node ID. Only applicable to cluster instances. If not specified, logs from the primary node are returned by default. Returns: @@ -744,12 +744,12 @@ async def describe_error_logs( """ Query error logs of an RDS instance. Args: - region_id (str): The region ID of the RDS instance. - db_instance_id (str): The ID of the RDS instance. - start_time (str): The start time of the query. Format: yyyy-MM-dd HH:mm. - end_time (str): The end time of the query. Format: yyyy-MM-dd HH:mm. - page_size (int): The number of records per page. Range: 30~100. Default: 30. - page_number (int): The page number. Default: 1. + region_id: The region ID of the RDS instance. + db_instance_id: The ID of the RDS instance. + start_time: The start time of the query. Format: yyyy-MM-dd HH:mm. + end_time: The end time of the query. Format: yyyy-MM-dd HH:mm. + page_size: The number of records per page. Range: 30~100. Default: 30. + page_number: The page number. Default: 1. Returns: Dict[str, Any]: A dictionary containing error log information """ @@ -784,8 +784,8 @@ async def describe_db_instance_net_info( """ Batch retrieves network configuration details for multiple RDS instances. Args: - region_id (str): The region ID of the RDS instance. - db_instance_ids (list[str]): List of DB instance identifiers (e.g., ["rm-uf6wjk5****", "db-instance-01"]) + region_id: The region ID of the RDS instance. + db_instance_ids: List of DB instance identifiers (e.g., ["rm-uf6wjk5****", "db-instance-01"]) Returns: list[dict]: A list of dictionaries containing network configuration details for each instance. """ @@ -811,8 +811,8 @@ async def describe_db_instance_ip_allowlist( """ Batch retrieves IP allowlist configurations for multiple RDS instances. Args: - region_id (str): The region ID of the RDS instance. - db_instance_ids (list[str]): List of DB instance identifiers (e.g., ["rm-uf6wjk5****", "db-instance-01"]) + region_id: The region ID of the RDS instance. + db_instance_ids: List of DB instance identifiers (e.g., ["rm-uf6wjk5****", "db-instance-01"]) Returns: list[dict]: A list of dictionaries containing network configuration details for each instance. """ @@ -838,8 +838,8 @@ async def describe_db_instance_databases( """ Batch retrieves database information for multiple RDS instances. Args: - region_id (str): The region ID of the RDS instance. - db_instance_ids (list[str]): List of DB instance identifiers (e.g., ["rm-uf6wjk5****", "db-instance-01"]) + region_id: The region ID of the RDS instance. + db_instance_ids: List of DB instance identifiers (e.g., ["rm-uf6wjk5****", "db-instance-01"]) Returns: list[dict]: A list of dictionaries containing database information for each instance. """ @@ -865,8 +865,8 @@ async def describe_db_instance_accounts( """ Batch retrieves account information for multiple RDS instances. Args: - region_id (str): The region ID of the RDS instance. - db_instance_ids (list[str]): List of DB instance identifiers (e.g., ["rm-uf6wjk5****", "db-instance-01"]) + region_id: The region ID of the RDS instance. + db_instance_ids: List of DB instance identifiers (e.g., ["rm-uf6wjk5****", "db-instance-01"]) Returns: list[dict]: A list of dictionaries containing account information for each instance. """ From 568c0157747b7eb01a08e9f40d3444efdbf54292 Mon Sep 17 00:00:00 2001 From: "wenfeng.wf" Date: Sun, 27 Apr 2025 13:45:51 +0800 Subject: [PATCH 4/6] Add `describe_bills` tools. --- README.md | 1 + README_CN.md | 1 + pyproject.toml | 3 +- .../server.py | 90 +++++++++++-------- .../utils.py | 54 ++++++++++- uv.lock | 17 ++++ 6 files changed, 126 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index a7b85a5..978e537 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ Add the following configuration to the MCP client configuration file: * `describe_db_instance_accounts`: Batch retrieves account information for multiple RDS instances. * `describe_available_classes`: Query available instance classes and storage ranges. * `describe_available_zones`: Query available zones for RDS instances. +* `describe_bills`: Query the consumption summary of all product instances or billing items for a user within a specific billing period. * `describe_vpcs`: Query VPC list. * `describe_vswitches`: Query VSwitch list. * `describe_slow_log_records`: Query slow log records for an RDS instance. diff --git a/README_CN.md b/README_CN.md index 6cb23ed..f3ce401 100644 --- a/README_CN.md +++ b/README_CN.md @@ -86,6 +86,7 @@ git clone https://github.com/aliyun/alibabacloud-rds-openapi-mcp-server.git * `describe_db_instance_accounts`: 批量查询实例的账号信息 * `describe_available_classes`: 查询可用实例规格和存储范围 * `describe_available_zones`: 查询RDS可用区 +* `describe_bills`: 批量查询实例账单信息. * `describe_vpcs`: 查询VPC列表 * `describe_vswitches`: 查询虚拟交换机列表 * `describe_slow_log_records`: 查询RDS慢日志记录 diff --git a/pyproject.toml b/pyproject.toml index 965aa3e..7709386 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ description = "MCP server for RDS Services via OPENAPI." readme = "README.md" requires-python = ">=3.12" dependencies = [ + "alibabacloud-bssopenapi20171214>=5.0.0", "alibabacloud-rds20140815>=11.0.0", "alibabacloud-vpc20160428>=6.11.4", "httpx>=0.28.1", @@ -28,4 +29,4 @@ Source = "https://github.com/aliyun/alibaba-cloud-ops-mcp-server.git" packages = ["src/alibabacloud_rds_openapi_mcp_server"] [project.scripts] -alibabacloud-rds-openapi-mcp-server = "alibabacloud_rds_openapi_mcp_server.server:main" \ No newline at end of file +alibabacloud-rds-openapi-mcp-server = "alibabacloud_rds_openapi_mcp_server.server:main" diff --git a/src/alibabacloud_rds_openapi_mcp_server/server.py b/src/alibabacloud_rds_openapi_mcp_server/server.py index 1a0a6cd..d442862 100644 --- a/src/alibabacloud_rds_openapi_mcp_server/server.py +++ b/src/alibabacloud_rds_openapi_mcp_server/server.py @@ -3,56 +3,28 @@ import os import sys from datetime import datetime - from typing import Dict, Any, List + +from alibabacloud_bssopenapi20171214 import models as bss_open_api_20171214_models from alibabacloud_rds20140815 import models as rds_20140815_models -from alibabacloud_rds20140815.client import Client as RdsClient -from alibabacloud_tea_openapi.models import Config from alibabacloud_vpc20160428 import models as vpc_20160428_models -from alibabacloud_vpc20160428.client import Client as VpcClient from mcp.server.fastmcp import FastMCP + current_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.append(current_dir) -from utils import transform_to_iso_8601, transform_to_datetime, transform_perf_key, compress_json_array +from utils import (transform_to_iso_8601, + transform_to_datetime, + transform_perf_key, + compress_json_array, + get_rds_client, + get_vpc_client, + get_bill_client) logger = logging.getLogger(__name__) mcp = FastMCP("Alibaba Cloud RDS OPENAPI") -def get_rds_client(region_id: str): - config = Config( - access_key_id=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), - access_key_secret=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'), - region_id=region_id, - protocol="https", - connect_timeout=10 * 1000, - read_timeout=300 * 1000 - ) - client = RdsClient(config) - return client - - -def get_vpc_client(region_id: str) -> VpcClient: - """Get VPC client instance. - - Args: - region_id: The region ID for the VPC client. - - Returns: - VpcClient: The VPC client instance for the specified region. - """ - config = Config( - access_key_id=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), - access_key_secret=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'), - region_id=region_id, - protocol="https", - connect_timeout=10 * 1000, - read_timeout=300 * 1000 - ) - return VpcClient(config) - - class OpenAPIError(Exception): """Custom exception for RDS OpenAPI related errors.""" pass @@ -884,6 +856,48 @@ async def describe_db_instance_accounts( raise e +@mcp.tool() +async def describe_bills( + billing_cycles: list[str], + db_instance_id: str = None +) -> dict[str, Any]: + """ + Query the consumption summary of all product instances or billing items for a user within a specific billing period. + Args: + billing_cycles: bill cycle YYYY-MM, e.g. 2020-03 + db_instance_id: DB instance id (e.g., "rm-xxx") + Returns: + str: billing information. + """ + try: + client = get_bill_client("cn-hangzhou") + res = {} + for billing_cycle in billing_cycles: + has_next_token = True + next_token = None + items = [] + + while has_next_token: + describe_instance_bill_request = bss_open_api_20171214_models.DescribeInstanceBillRequest( + billing_cycle=billing_cycle, + product_code='rds', + next_token=next_token + ) + if db_instance_id: + describe_instance_bill_request.db_instance_id = db_instance_id + + response = client.describe_instance_bill(describe_instance_bill_request) + if not response.body.data: + break + next_token = response.body.data.next_token + has_next_token = next_token is not None and next_token.strip() != "" + items.extend(response.body.data.items) + res[billing_cycle] = compress_json_array([item.to_map() for item in items]) + return res + except Exception as e: + raise e + + @mcp.tool() async def get_current_time() -> Dict[str, Any]: """Get the current time. diff --git a/src/alibabacloud_rds_openapi_mcp_server/utils.py b/src/alibabacloud_rds_openapi_mcp_server/utils.py index db5c449..3ee8727 100644 --- a/src/alibabacloud_rds_openapi_mcp_server/utils.py +++ b/src/alibabacloud_rds_openapi_mcp_server/utils.py @@ -1,5 +1,10 @@ +import os from datetime import datetime, timezone +from alibabacloud_bssopenapi20171214.client import Client as BssOpenApi20171214Client +from alibabacloud_rds20140815.client import Client as RdsClient +from alibabacloud_tea_openapi.models import Config +from alibabacloud_vpc20160428.client import Client as VpcClient PERF_KEYS = { "mysql": { @@ -29,6 +34,7 @@ } + def transform_to_iso_8601(dt: datetime, timespec: str): return dt.astimezone(timezone.utc).isoformat(timespec=timespec).replace("+00:00", "Z") @@ -56,5 +62,51 @@ def compress_json_array(json_array: list[dict]): return "" compress_str = ";".join(json_array[0].keys()) for item in json_array: - compress_str += "|" + ";".join([str(item[key]) for key in json_array[0].keys()]) + compress_str += "|" + ";".join([str(item[key] if key in item else "") for key in json_array[0].keys()]) return compress_str + + +def get_rds_client(region_id: str): + config = Config( + access_key_id=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), + access_key_secret=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'), + region_id=region_id, + protocol="https", + connect_timeout=10 * 1000, + read_timeout=300 * 1000 + ) + client = RdsClient(config) + return client + + +def get_vpc_client(region_id: str) -> VpcClient: + """Get VPC client instance. + + Args: + region_id: The region ID for the VPC client. + + Returns: + VpcClient: The VPC client instance for the specified region. + """ + config = Config( + access_key_id=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), + access_key_secret=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'), + region_id=region_id, + protocol="https", + connect_timeout=10 * 1000, + read_timeout=300 * 1000 + ) + return VpcClient(config) + + +def get_bill_client(region_id: str): + config = Config( + access_key_id=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), + access_key_secret=os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'), + region_id=region_id, + protocol="https", + connect_timeout=10 * 1000, + read_timeout=300 * 1000 + ) + client = BssOpenApi20171214Client(config) + return client diff --git a/uv.lock b/uv.lock index f9670bb..83d9464 100644 --- a/uv.lock +++ b/uv.lock @@ -72,6 +72,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, ] +[[package]] +name = "alibabacloud-bssopenapi20171214" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alibabacloud-endpoint-util" }, + { name = "alibabacloud-openapi-util" }, + { name = "alibabacloud-tea-openapi" }, + { name = "alibabacloud-tea-util" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/91/8a5dd7be8fc9c280aaa2fa6a6e03e5b282179664ab9f4badb96e0ce8abc0/alibabacloud_bssopenapi20171214-5.0.0.tar.gz", hash = "sha256:76dcf00a5a834d66f5eca061e56b7d5bba3fd9dea796efbbe4c4900d18bd4f3a", size = 139590 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/9c/74eeb1f7eb6f96a4d6ee8b47ad1469d05df9215f242935aad9dae370b48c/alibabacloud_bssopenapi20171214-5.0.0-py3-none-any.whl", hash = "sha256:1b45fa23ff8dd4ab650b9ae42cda3c12423f1c33d22fea011212bdac75998f20", size = 140975 }, +] + [[package]] name = "alibabacloud-credentials" version = "0.3.6" @@ -114,6 +129,7 @@ name = "alibabacloud-rds-openapi-mcp-server" version = "1.2.2" source = { virtual = "." } dependencies = [ + { name = "alibabacloud-bssopenapi20171214" }, { name = "alibabacloud-rds20140815" }, { name = "alibabacloud-vpc20160428" }, { name = "httpx" }, @@ -122,6 +138,7 @@ dependencies = [ [package.metadata] requires-dist = [ + { name = "alibabacloud-bssopenapi20171214", specifier = ">=5.0.0" }, { name = "alibabacloud-rds20140815", specifier = ">=11.0.0" }, { name = "alibabacloud-vpc20160428", specifier = ">=6.11.4" }, { name = "httpx", specifier = ">=0.28.1" }, From 2df50b9a516162c03a76641d890dfc8e4b61f341 Mon Sep 17 00:00:00 2001 From: "wenfeng.wf" Date: Sun, 27 Apr 2025 14:14:48 +0800 Subject: [PATCH 5/6] support compress performance items. --- .../server.py | 19 +++++++++++++++++-- .../utils.py | 13 +++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/alibabacloud_rds_openapi_mcp_server/server.py b/src/alibabacloud_rds_openapi_mcp_server/server.py index d442862..76572c9 100644 --- a/src/alibabacloud_rds_openapi_mcp_server/server.py +++ b/src/alibabacloud_rds_openapi_mcp_server/server.py @@ -78,10 +78,25 @@ async def describe_db_instance_performance(region_id: str, region_id: db instance region(e.g. cn-hangzhou) db_instance_id: db instance id(e.g. rm-xxx) db_type: the db instance database type(e.g. mysql,pgsql,sqlserver) - perf_keys: Performance Key (e.g. ["MemCpuUsage", "QPSTPS", "Sessions", "COMDML", "RowDML"]) + perf_keys: Performance Key (e.g. ["MemCpuUsage", "QPSTPS", "Sessions", "COMDML", "RowDML", "ThreadStatus", "MBPS", "DetailedSpaceUsage"]) start_time: start time(e.g. 2023-01-01 00:00) end_time: end time(e.g. 2023-01-01 00:00) """ + def _compress_performance(performance_value, max_items=10): + if len(performance_value) > max_items: + result = [] + offset = len(performance_value) / 10 + for i in range(0, len(performance_value), int(offset)): + _item = None + for j in range(i, min(i + int(offset), len(performance_value))): + if _item is None or sum([float(v) for v in performance_value[j].value.split('&')]) > sum([float(v) for v in _item.value.split('&')]): + _item = performance_value[j] + else: + result.append(_item) + return result + else: + return performance_value + try: start_time = transform_to_datetime(start_time) end_time = transform_to_datetime(end_time) @@ -98,7 +113,7 @@ async def describe_db_instance_performance(region_id: str, response = client.describe_dbinstance_performance(request) responses = [] for perf_key in response.body.performance_keys.performance_key: - perf_key_info = f"""Key={perf_key.key}; Unit={perf_key.unit}; ValueFormat={perf_key.value_format}; Values={compress_json_array([item.to_map() for item in perf_key.values.performance_value])}""" + perf_key_info = f"""Key={perf_key.key}; Unit={perf_key.unit}; ValueFormat={perf_key.value_format}; Values={compress_json_array([item.to_map() for item in _compress_performance(perf_key.values.performance_value)])}""" responses.append(perf_key_info) return responses except Exception as e: diff --git a/src/alibabacloud_rds_openapi_mcp_server/utils.py b/src/alibabacloud_rds_openapi_mcp_server/utils.py index 3ee8727..f24f6eb 100644 --- a/src/alibabacloud_rds_openapi_mcp_server/utils.py +++ b/src/alibabacloud_rds_openapi_mcp_server/utils.py @@ -13,7 +13,10 @@ "Sessions": ["MySQL_Sessions"], "COMDML": ["MySQL_COMDML"], "RowDML": ["MySQL_RowDML"], - "SpaceUsage": ["MySQL_DetailedSpaceUsage"] + "SpaceUsage": ["MySQL_DetailedSpaceUsage"], + "ThreadStatus": ["MySQL_ThreadStatus"], + "MBPS": ["MySQL_MBPS"], + "DetailedSpaceUsage": ["MySQL_DetailedSpaceUsage"] }, "pgsql": { "MemCpuUsage": ["MemoryUsage", "CpuUsage"], @@ -21,7 +24,10 @@ "Sessions": ["PgSQL_Session"], "COMDML": ["PgSQL_COMDML"], "RowDML": ["PolarDBRowDML"], - "SpaceUsage": ["PgSQL_SpaceUsage"] + "SpaceUsage": ["PgSQL_SpaceUsage"], + "ThreadStatus": [], + "MBPS": [], + "DetailedSpaceUsage": ["SQLServer_DetailedSpaceUsage"] }, "sqlserver": { "MemCpuUsage": ["SQLServer_CPUUsage"], @@ -30,6 +36,9 @@ "COMDML": [], "RowDML": [], "SpaceUsage": ["SQLServer_DetailedSpaceUsage"], + "ThreadStatus": [], + "MBPS": [], + "DetailedSpaceUsage": ["PgSQL_SpaceUsage"] } } From dd02c5c5b8f84c7cd2a6e638abf8823d7bc5bda9 Mon Sep 17 00:00:00 2001 From: "wenfeng.wf" Date: Sun, 27 Apr 2025 14:18:31 +0800 Subject: [PATCH 6/6] Update version. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7709386..33885e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "alibabacloud-rds-openapi-mcp-server" -version = "1.2.2" +version = "1.3.0" description = "MCP server for RDS Services via OPENAPI." readme = "README.md" requires-python = ">=3.12"