Skip to main content
Glama
aliyun

Alibaba Cloud RDS OpenAPI MCP Server

Official
by aliyun

query_sql

Read-only

Execute read-only SQL queries on Alibaba Cloud RDS databases to retrieve data or show database information using instance ID, region, and database name.

Instructions

execute read-only sql likes show xxx, select xxx
Args:
    dbinstance_id (str): The ID of the RDS instance.
    region_id(str): the region id of instance.
    db_name(str): the db name for execute sql.
    sql(str): the sql to be executed.
Returns:
    the sql result.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
region_idYes
dbinstance_idYes
db_nameYes
sqlYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The core handler function for the 'query_sql' MCP tool. It creates a temporary read-only database account if needed, connects to the specified RDS instance and database, executes the provided read-only SQL query, and returns the results as JSON.
    async def query_sql(
            region_id: str,
            dbinstance_id: str,
            db_name: str,
            sql: str
    ) -> str:
        """
        execute read-only sql likes show xxx, select xxx
        Args:
            dbinstance_id (str): The ID of the RDS instance.
            region_id(str): the region id of instance.
            db_name(str): the db name for execute sql.
            sql(str): the sql to be executed.
        Returns:
            the sql result.
        """
        try:
            async with DBService(region_id, dbinstance_id, db_name) as service:
                return await service.execute_sql(sql=sql)
        except Exception as e:
            logger.error(f"Error occurred: {str(e)}")
            raise e
  • The DBConn.execute_sql method, which performs the actual SQL execution and formats results as JSON for the query_sql tool.
    def execute_sql(self, sql):
        cursor = self.conn.cursor()
        cursor.execute(sql)
        columns = [desc[0] for desc in cursor.description]
        rows = cursor.fetchall()
        if self.dbtype == 'mysql':
            result = [dict(row) for row in rows]
        elif self.dbtype == 'postgresql' or self.dbtype == 'pg':
            result = [dict(zip(columns, row)) for row in rows]
        elif self.dbtype == 'sqlserver':
            result = [dict(zip(columns, row)) for row in rows]
        else:
            result = []
        cursor.close()
        try:
            return json.dumps(result, ensure_ascii=False)
        except Exception as e:
            return str(result)
  • The DBService context manager class used by query_sql. It fetches instance details, creates a temporary read-only account if necessary, grants privileges, establishes DB connection, executes SQL via DBConn, and cleans up afterward.
    class DBService:
        """
        Create a read-only account, execute the SQL statements, and automatically delete the account afterward.
        """
        def __init__(self,
                     region_id,
                     instance_id,
                     database=None, ):
            self.instance_id = instance_id
            self.database = database
            self.region_id = region_id
    
            self.__db_type = None
            self.__account_name, self.__account_password = get_rds_account()
            self.__host = None
            self.__port = None
            self.__client = get_rds_client(region_id)
            self.__db_conn = None
    
        async def __aenter__(self):
            await asyncio.to_thread(self._get_db_instance_info)
            if not self.__account_name or not self.__account_password:
                await asyncio.to_thread(self._create_temp_account)
                if self.database:
                    await asyncio.to_thread(self._grant_privilege)
            else:
                self.account_name = self.__account_name
                self.account_password = self.__account_password
            self.__db_conn = DBConn(self)
            await asyncio.to_thread(self.__db_conn.connect)
            return self
    
        async def __aexit__(self, exc_type, exc_val, exc_tb):
            if self.__db_conn is not None:
                await asyncio.to_thread(self.__db_conn.close)
            if not self.__account_name or not self.__account_password:
                await asyncio.to_thread(self._delete_account)
            self.__client = None
    
        def _get_db_instance_info(self):
            req = rds_20140815_models.DescribeDBInstanceAttributeRequest(
                dbinstance_id=self.instance_id,
            )
            self.__client.describe_dbinstance_attribute(req)
            resp = self.__client.describe_dbinstance_attribute(req)
            self.db_type = resp.body.items.dbinstance_attribute[0].engine.lower()
    
            req = rds_20140815_models.DescribeDBInstanceNetInfoRequest(
                dbinstance_id=self.instance_id,
            )
            resp = self.__client.describe_dbinstance_net_info(req)
    
            # 取支持的地址:
            vpc_host, vpc_port, public_host, public_port, dbtype = None, None, None, None, None
            net_infos = resp.body.dbinstance_net_infos.dbinstance_net_info
            for item in net_infos:
                if 'Private' == item.iptype:
                    vpc_host = item.connection_string
                    vpc_port = int(item.port)
                elif 'Public' in item.iptype:
                    public_host = item.connection_string
                    public_port = int(item.port)
    
            if vpc_host and test_connect(vpc_host, vpc_port):
                self.host = vpc_host
                self.port = vpc_port
            elif public_host and test_connect(public_host, public_port):
                self.host = public_host
                self.port = public_port
            else:
                raise Exception('connection db failed.')
    
        def _create_temp_account(self):
            self.account_name = 'mcp_' + random_str(10)
            self.account_password = random_password(32)
            request = rds_20140815_models.CreateAccountRequest(
                dbinstance_id=self.instance_id,
                account_name=self.account_name,
                account_password=self.account_password,
                account_description="Created by mcp for execute sql."
            )
            self.__client.create_account(request)
    
        def _grant_privilege(self):
            req = rds_20140815_models.GrantAccountPrivilegeRequest(
                dbinstance_id=self.instance_id,
                account_name=self.account_name,
                dbname=self.database,
                account_privilege="ReadOnly" if self.db_type.lower() in ('mysql', 'postgresql') else "DBOwner"
            )
            self.__client.grant_account_privilege(req)
    
        def _delete_account(self):
            if not self.account_name:
                return
            req = rds_20140815_models.DeleteAccountRequest(
                dbinstance_id=self.instance_id,
                account_name=self.account_name
            )
            self.__client.delete_account(req)
    
        async def execute_sql(self, sql):
            return await asyncio.to_thread(self.__db_conn.execute_sql, sql)
    
        @property
        def user(self):
            return self.account_name
    
        @property
        def password(self):
            return self.account_password
Behavior3/5

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

The description adds value by specifying 'read-only sql', which aligns with the readOnlyHint=true annotation. It also implies execution on a specific database instance, adding context beyond annotations. However, it lacks details on rate limits, error handling, or result formatting, which would be helpful for behavioral transparency.

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

Conciseness3/5

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

The description is front-loaded with the core purpose, followed by parameter details in a structured format. However, it includes redundant phrasing ('sql to be executed') and could be more streamlined. The structure is functional but not optimally concise.

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 the tool's complexity (SQL execution with 4 parameters), annotations cover safety (read-only), and an output schema exists, the description is minimally adequate. It explains parameters and return values but lacks usage context, error details, or examples, leaving gaps for the agent to navigate.

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?

With 0% schema description coverage, the description compensates by listing all 4 parameters with brief explanations (e.g., 'the db name for execute sql'). This adds meaning beyond the bare schema, but the explanations are minimal and don't cover formats, constraints, or examples, keeping it at a baseline level.

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 tool's purpose as 'execute read-only sql' with examples (show xxx, select xxx), which is specific and distinguishes it from write operations. However, it doesn't explicitly differentiate from sibling tools like 'explain_sql' or 'show_create_table', which also involve SQL execution but for different purposes.

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?

The description provides no guidance on when to use this tool versus alternatives like 'explain_sql' or 'show_create_table'. It mentions 'read-only sql' but doesn't specify scenarios, prerequisites, or exclusions, leaving the agent to infer usage from context alone.

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/aliyun/alibabacloud-rds-openapi-mcp-server'

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