This is an automated email from the ASF dual-hosted git repository.

fanng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 7069bbd3b7 [#8093] improvement(mcp-server): Use pylint to format code 
in mcp-server (#8094)
7069bbd3b7 is described below

commit 7069bbd3b71e173d0a5dddcaede33c2b312493a5
Author: Mini Yu <[email protected]>
AuthorDate: Fri Aug 15 10:09:18 2025 +0800

    [#8093] improvement(mcp-server): Use pylint to format code in mcp-server 
(#8094)
    
    ### What changes were proposed in this pull request?
    
    Use pylint to format code in mcp-server.
    
    ### Why are the changes needed?
    
    For high-quality code.
    
    Fix: #8093
    
    ### Does this PR introduce _any_ user-facing change?
    
    N/A.
    
    ### How was this patch tested?
    
    Test locally and CI
---
 mcp-server/build.gradle.kts                        |  6 ++
 mcp-server/mcp_server/client/fileset_operation.py  |  1 +
 .../client/plain/exception.py}                     | 24 +-----
 .../plain/plain_rest_client_fileset_operation.py   |  4 +-
 .../plain/plain_rest_client_model_operation.py     |  9 +-
 .../plain/plain_rest_client_tag_operation.py       |  5 +-
 mcp-server/mcp_server/client/plain/utils.py        |  6 +-
 mcp-server/mcp_server/client/tag_operation.py      | 16 ++--
 mcp-server/mcp_server/main.py                      |  9 +-
 mcp-server/mcp_server/server.py                    |  3 +-
 mcp-server/mcp_server/tools/catalog.py             | 19 +++--
 mcp-server/mcp_server/tools/fileset.py             |  1 +
 mcp-server/mcp_server/tools/model.py               |  3 +-
 mcp-server/mcp_server/tools/table.py               | 38 ++++++++-
 mcp-server/pylintrc                                | 95 ++++++++++++++++++++++
 mcp-server/pyproject.toml                          |  1 +
 mcp-server/tests/unit/tools/mock_operation.py      | 14 ++--
 mcp-server/tests/unit/tools/test_catalog.py        |  5 ++
 mcp-server/uv.lock                                 | 85 ++++++++++++++++++-
 19 files changed, 285 insertions(+), 59 deletions(-)

diff --git a/mcp-server/build.gradle.kts b/mcp-server/build.gradle.kts
index 14e416a5a6..0aa3dd8d0f 100644
--- a/mcp-server/build.gradle.kts
+++ b/mcp-server/build.gradle.kts
@@ -250,6 +250,11 @@ tasks {
   }
 }
 
+tasks.register<Exec>("pylint") {
+  mustRunAfter("buildPython")
+  commandLine(venvPython, "-m", "pylint", "./tests", "./mcp_server")
+}
+
 tasks.named("test") {
   val skipUTs = project.hasProperty("skipTests")
   if (!skipUTs) {
@@ -259,6 +264,7 @@ tasks.named("test") {
 
 tasks.named("build") {
   dependsOn("buildPython")
+  dependsOn("pylint")
 }
 
 tasks.named("clean") {
diff --git a/mcp-server/mcp_server/client/fileset_operation.py 
b/mcp-server/mcp_server/client/fileset_operation.py
index 9350bc128d..e41b3ec74f 100644
--- a/mcp-server/mcp_server/client/fileset_operation.py
+++ b/mcp-server/mcp_server/client/fileset_operation.py
@@ -56,6 +56,7 @@ class FilesetOperation(ABC):
         """
         pass
 
+    # pylint: disable=too-many-positional-arguments
     @abstractmethod
     async def list_files_in_fileset(
         self,
diff --git a/mcp-server/pyproject.toml 
b/mcp-server/mcp_server/client/plain/exception.py
similarity index 64%
copy from mcp-server/pyproject.toml
copy to mcp-server/mcp_server/client/plain/exception.py
index 5c66261caa..25b085778a 100644
--- a/mcp-server/pyproject.toml
+++ b/mcp-server/mcp_server/client/plain/exception.py
@@ -15,26 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-[project]
-name = "gravitino_mcp_server"
-version = "1.0.0"
-description = "Gravitino MCP server"
-readme = "README.md"
-requires-python = ">=3.10"
-dependencies = [
-    "fastmcp>=2.10.6",
-    "parameterized>=0.9.0",
-    "pytest>=8.4.1",
-]
 
-[tool.isort]
-profile = "black"
-line_length = 80
-multi_line_output = 3
-include_trailing_comma = true
-force_grid_wrap = 0
-use_parentheses = true
+class GravitinoException(Exception):
+    """Custom exception for Gravitino-related errors."""
 
-[tool.black]
-line-length = 80
-target-version = ['py38']
+    pass
diff --git 
a/mcp-server/mcp_server/client/plain/plain_rest_client_fileset_operation.py 
b/mcp-server/mcp_server/client/plain/plain_rest_client_fileset_operation.py
index 3d53be529e..9548d7aab2 100644
--- a/mcp-server/mcp_server/client/plain/plain_rest_client_fileset_operation.py
+++ b/mcp-server/mcp_server/client/plain/plain_rest_client_fileset_operation.py
@@ -39,6 +39,7 @@ class PlainRESTClientFilesetOperation(FilesetOperation):
         )
         return response.json().get("fileset", {})
 
+    # pylint: disable=too-many-positional-arguments
     async def list_files_in_fileset(
         self,
         catalog_name: str,
@@ -48,6 +49,7 @@ class PlainRESTClientFilesetOperation(FilesetOperation):
         sub_path: str = "/",
     ) -> str:
         response = await self.rest_client.get(
-            
f"/api/metalakes/{self.metalake_name}/catalogs/{catalog_name}/schemas/{schema_name}/filesets/{fileset_name}/files?sub_path={sub_path}&location_name={location_name}"
+            
f"/api/metalakes/{self.metalake_name}/catalogs/{catalog_name}/schemas/{schema_name}"
+            
f"/filesets/{fileset_name}/files?sub_path={sub_path}&location_name={location_name}"
         )
         return response.json().get("files", [])
diff --git 
a/mcp-server/mcp_server/client/plain/plain_rest_client_model_operation.py 
b/mcp-server/mcp_server/client/plain/plain_rest_client_model_operation.py
index 3b63c63d75..266865973b 100644
--- a/mcp-server/mcp_server/client/plain/plain_rest_client_model_operation.py
+++ b/mcp-server/mcp_server/client/plain/plain_rest_client_model_operation.py
@@ -47,7 +47,8 @@ class PlainRESTClientModelOperation(ModelOperation):
         self, catalog_name: str, schema_name: str, model_name: str
     ) -> str:
         response = await self.rest_client.get(
-            
f"/api/metalakes/{self.metalake_name}/catalogs/{catalog_name}/schemas/{schema_name}/models/{model_name}/versions?details=true"
+            
f"/api/metalakes/{self.metalake_name}/catalogs/{catalog_name}/schemas/{schema_name}/models/{model_name}"
+            f"/versions?details=true"
         )
         return response.json().get("infos", [])
 
@@ -55,7 +56,8 @@ class PlainRESTClientModelOperation(ModelOperation):
         self, catalog_name: str, schema_name: str, model_name: str, version: 
int
     ) -> str:
         response = await self.rest_client.get(
-            
f"/api/metalakes/{self.metalake_name}/catalogs/{catalog_name}/schemas/{schema_name}/models/{model_name}/versions/{version}"
+            
f"/api/metalakes/{self.metalake_name}/catalogs/{catalog_name}/schemas/{schema_name}/models/{model_name}"
+            f"/versions/{version}"
         )
         return response.json().get("modelVersion", {})
 
@@ -63,6 +65,7 @@ class PlainRESTClientModelOperation(ModelOperation):
         self, catalog_name: str, schema_name: str, model_name: str, alias: str
     ) -> str:
         response = await self.rest_client.get(
-            
f"/api/metalakes/{self.metalake_name}/catalogs/{catalog_name}/schemas/{schema_name}/models/{model_name}/aliases/{alias}"
+            
f"/api/metalakes/{self.metalake_name}/catalogs/{catalog_name}/schemas/{schema_name}/models/{model_name}/"
+            f"aliases/{alias}"
         )
         return response.json().get("modelVersion", {})
diff --git 
a/mcp-server/mcp_server/client/plain/plain_rest_client_tag_operation.py 
b/mcp-server/mcp_server/client/plain/plain_rest_client_tag_operation.py
index 13493307aa..cd3a67bcb0 100644
--- a/mcp-server/mcp_server/client/plain/plain_rest_client_tag_operation.py
+++ b/mcp-server/mcp_server/client/plain/plain_rest_client_tag_operation.py
@@ -15,6 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
+from mcp_server.client.plain.exception import GravitinoException
 from mcp_server.client.plain.utils import extract_content_from_response
 from mcp_server.client.tag_operation import TagOperation
 
@@ -63,7 +64,9 @@ class PlainRESTClientTagOperation(TagOperation):
             f"/api/metalakes/{self.metalake_name}/tags/{name}"
         )
         if response.status_code != 200:
-            raise Exception(f"Failed to delete tag {name}: {response.text}")
+            raise GravitinoException(
+                f"Failed to delete tag {name}: {response.text}"
+            )
         return None
 
     async def associate_tag_with_metadata(
diff --git a/mcp-server/mcp_server/client/plain/utils.py 
b/mcp-server/mcp_server/client/plain/utils.py
index 6d93d8813e..ab0d197229 100644
--- a/mcp-server/mcp_server/client/plain/utils.py
+++ b/mcp-server/mcp_server/client/plain/utils.py
@@ -18,6 +18,8 @@
 import json
 import logging
 
+from mcp_server.client.plain.exception import GravitinoException
+
 
 def extract_content_from_response(response, field: str, default="") -> str:
     response_json = response.json()
@@ -31,5 +33,5 @@ def _handle_gravitino_exception(response: dict):
         t = response.get("type", "")
         message = response.get("message", "")
         error_message = f"Error code: {error_code}, Error type: {t}, Error 
message: {message}"
-        logging.warn(error_message)
-        raise Exception(error_message)
+        logging.warning(error_message)
+        raise GravitinoException(error_message)
diff --git a/mcp-server/mcp_server/client/tag_operation.py 
b/mcp-server/mcp_server/client/tag_operation.py
index 613dda7f5a..b00a06b8f4 100644
--- a/mcp-server/mcp_server/client/tag_operation.py
+++ b/mcp-server/mcp_server/client/tag_operation.py
@@ -25,15 +25,15 @@ class TagOperation(ABC):
 
     @abstractmethod
     async def create_tag(
-        self, name: str, comment: str, properties: dict
+        self, tag_name: str, tag_comment: str, tag_properties: dict
     ) -> str:
         """
         Create a new tag within the specified metalake.
 
         Args:
-            name: Name of the tag to be created
-            comment: Description or comment for the tag
-            properties: Dictionary of key-value pairs representing tag 
properties
+            tag_name: Name of the tag to be created
+            tag_comment: Description or comment for the tag
+            tag_properties: Dictionary of key-value pairs representing tag 
properties
 
         Returns:
             str: JSON-formatted string containing the created tag information
@@ -41,12 +41,12 @@ class TagOperation(ABC):
         pass
 
     @abstractmethod
-    async def get_tag_by_name(self, name: str) -> str:
+    async def get_tag_by_name(self, tag_name: str) -> str:
         """
         Load a tag by its name.
 
         Args:
-            name: Name of the tag to get
+            tag_name: Name of the tag to get
 
         Returns:
             str: JSON-formatted string containing the tag information
@@ -64,12 +64,12 @@ class TagOperation(ABC):
         pass
 
     @abstractmethod
-    async def alter_tag(self, name: str, updates: list) -> str:
+    async def alter_tag(self, tag_name: str, updates: list) -> str:
         """
         Alter an existing tag within the specified metalake.
 
         Args:
-            name: Name of the tag to be altered
+            tag_name: Name of the tag to be altered
             updates: List of update operations to be applied to the tag
 
         Returns:
diff --git a/mcp-server/mcp_server/main.py b/mcp-server/mcp_server/main.py
index 6b7a80d758..c325ea17c4 100644
--- a/mcp-server/mcp_server/main.py
+++ b/mcp-server/mcp_server/main.py
@@ -32,7 +32,7 @@ def do_main():
         mcp_url=args.mcp_url,
     )
     _init_logging(setting)
-    logging.info(f"Gravitino MCP server setting: {setting}")
+    logging.info("Gravitino MCP server setting: %s", setting)
     server = GravitinoMCPServer(setting)
     server.run()
 
@@ -47,7 +47,6 @@ def _init_logging(setting: Setting):
 
 
 def _comma_separated_set(value) -> set:
-    print(f"value={value}")
     if not value:
         return set()
     return set(item.strip() for item in value.split(",") if item.strip())
@@ -76,7 +75,8 @@ def _parse_args():
         "--include-tool-tags",
         type=_comma_separated_set,
         default=set(),
-        help="The tool tags to include, separated by commas, support 
tags:[catalog, schema, table]. (default: empty, all tools will be included).",
+        help="The tool tags to include, separated by commas, support 
tags:[catalog, schema, table]. "
+        "(default: empty, all tools will be included).",
     )
 
     parser.add_argument(
@@ -84,7 +84,8 @@ def _parse_args():
         type=str,
         choices=["stdio", "http"],
         default=DefaultSetting.default_transport,
-        help=f"Transport protocol type: stdio (local), http (Streamable HTTP). 
(default: {DefaultSetting.default_transport})",
+        help=f"Transport protocol type: stdio (local), http (Streamable HTTP). 
"
+        f"(default: {DefaultSetting.default_transport})",
     )
 
     parser.add_argument(
diff --git a/mcp-server/mcp_server/server.py b/mcp-server/mcp_server/server.py
index 3020716af5..9cce5f556b 100644
--- a/mcp-server/mcp_server/server.py
+++ b/mcp-server/mcp_server/server.py
@@ -17,7 +17,6 @@
 
 import asyncio
 import logging
-import re
 from contextlib import asynccontextmanager
 from typing import AsyncIterator
 from urllib.parse import urlparse
@@ -38,7 +37,7 @@ def _create_lifespan_manager(gravitino_context: 
GravitinoContext):
 
     @asynccontextmanager
     async def app_lifespan(server: FastMCP) -> AsyncIterator[GravitinoContext]:
-        logging.info(f"Add Gravitino context: {gravitino_context}")
+        logging.info("Add Gravitino context: %s", gravitino_context)
         yield gravitino_context
 
     return app_lifespan
diff --git a/mcp-server/mcp_server/tools/catalog.py 
b/mcp-server/mcp_server/tools/catalog.py
index dd025f4d94..50115610e8 100644
--- a/mcp-server/mcp_server/tools/catalog.py
+++ b/mcp-server/mcp_server/tools/catalog.py
@@ -34,16 +34,16 @@ def load_catalog_tools(mcp: FastMCP):
             str: A JSON string representing an array of catalog objects with 
the following structure:
                 [
                     {
-                        "name": "catalog-name",            # A unique name for 
the catalog
-                        "type": "catalog-type",             # Type of catalog 
(e.g., "relational", "fileset", "message", "model")
-                        "provider": "provider-name",       # The catalog 
provider. One catalog type may have different providers, for "relational" 
catalog type, the provider may be "jdbc-postgresql", "jdbc-mysql", 
"lakehouse-iceberg", etc.
-                        "comment": "description-text",      # Human-readable 
description
-                        "properties": {                     # Configuration 
properties, the property key may differ for different catalog types and 
providers
+                        "name": "catalog-name",
+                        "type": "catalog-type",
+                        "provider": "provider-name",
+                        "comment": "description-text",
+                        "properties": {
                             "key1": "value1",
                             "key2": "value2",
                             ...
                         },
-                        "audit": {                         # Audit metadata
+                        "audit": {
                             "creator": "creator-name",
                             "createTime": "ISO-8601-timestamp",
                             "lastModifier": "modifier-name",
@@ -53,6 +53,13 @@ def load_catalog_tools(mcp: FastMCP):
                     ...
                 ]
 
+                name: The unique name of the catalog.
+                type: The type of the catalog, such as "relational", 
"fileset", "message", or "model".
+                provider: The specific provider of the catalog, which can vary 
based on the catalog type.
+                comment: A human-readable description of the catalog.
+                properties: A dictionary of configuration properties for the 
catalog,
+                            which may vary based on the catalog type and 
provider.
+
         Example Return Value:
             [
               {
diff --git a/mcp-server/mcp_server/tools/fileset.py 
b/mcp-server/mcp_server/tools/fileset.py
index 2bfc53734d..27400de207 100644
--- a/mcp-server/mcp_server/tools/fileset.py
+++ b/mcp-server/mcp_server/tools/fileset.py
@@ -96,6 +96,7 @@ def load_fileset_tools(mcp: FastMCP):
             catalog_name, schema_name, fileset_name
         )
 
+    # pylint:disable=too-many-positional-arguments
     @mcp.tool(tags={"fileset"})
     async def list_files_in_fileset(
         ctx: Context,
diff --git a/mcp-server/mcp_server/tools/model.py 
b/mcp-server/mcp_server/tools/model.py
index 9e3ac18dc7..08e56343da 100644
--- a/mcp-server/mcp_server/tools/model.py
+++ b/mcp-server/mcp_server/tools/model.py
@@ -121,7 +121,8 @@ def load_model_tools(mcp: FastMCP):
             model_name (str): The name of the model to list versions for.
 
         Returns:
-            str: A JSON string containing model version information. The 
string represents an array of version information.
+            str: A JSON string containing model version information. The string
+                represents an array of version information.
 
         Example Return Value:
             [
diff --git a/mcp-server/mcp_server/tools/table.py 
b/mcp-server/mcp_server/tools/table.py
index 5b7f1836dc..af6c62da6f 100644
--- a/mcp-server/mcp_server/tools/table.py
+++ b/mcp-server/mcp_server/tools/table.py
@@ -109,7 +109,7 @@ def load_table_tools(mcp: FastMCP):
                         },
                         ...
                     ],
-                    "properties": {                        # Table-specific 
properties, different catalog provider may have different properties.
+                    "properties": {
                         "key1": "value1",
                         "key2": "value2",
                         ...
@@ -148,6 +148,31 @@ def load_table_tools(mcp: FastMCP):
                         ...
                     ]
                 }
+                name: The unique name of the table.
+                comment: A human-readable description of the table.
+                columns: A list of column definitions, each containing:
+                        - name: The name of the column.
+                        - type: The data type of the column (e.g., string, 
integer).
+                        - comment: A description of the column.
+                        - nullable: Indicates whether the column can contain 
null values.
+                        - autoIncrement: Indicates whether the column is 
auto-incrementing.
+                properties: A dictionary of table-specific properties, which 
may vary based on the catalog provider.
+                audit: Metadata about the table's creation, including:
+                        - creator: The name of the user who created the table.
+                        - createTime: The timestamp when the table was 
created, in ISO-8601 format.
+                distribution: Information about how the data is distributed 
across files/parts, including:
+                        - strategy: The distribution strategy used (e.g., 
hash, range).
+                        - number: The number of buckets or parts created.
+                sortOrders: A list of sorting specifications for the table, 
each containing:
+                        - sortTerm: The term used for sorting, which can be a 
field or an expression.
+                        - direction: The sorting direction (ascending or 
descending).
+                        - nullOrdering: How null values are ordered (nulls 
first or last).
+
+                partitioning: A list of partitioning strategies used for the 
table, each containing:
+                        - strategy: The partitioning strategy used (e.g., 
identity, bucket[N], truncate[W], list,
+                                    range, func).
+                        - fieldName: The field(s) used for partitioning, 
specified as a list of strings.
+                indexes: A list of table indexes, such as primary keys or 
unique keys.
 
         Example Return Value:
             {
@@ -217,8 +242,15 @@ def load_table_tools(mcp: FastMCP):
         Special Considerations:
             - The catalog type which containing the table must be "relational"
             - The table in different catalog provider may have different 
properties
-            - "distribution" a.k.a (Clustering) is a technique to split the 
data into more manageable files/parts, (By specifying the number of buckets to 
create). The value of the distribution column will be hashed by a user-defined 
number into buckets. Supporting "hash", "range", "even", etc distribution 
strategies.
-            - "partitioning" is a partitioning strategy that is used to split 
a table into parts based on partition keys. Supporting diverse partitioning 
strategies like "identity", "bucket[N]", "truncate[W]", "list", "range", 
"func", etc.
+            - "distribution" a.k.a (Clustering) is a technique to split the 
data
+                into more manageable files/parts, (By specifying the number of
+                buckets to create). The value of the distribution column will 
be
+                hashed by a user-defined number into buckets. Supporting 
"hash",
+                "range", "even", etc distribution strategies.
+            - "partitioning" is a partitioning strategy that is used to split a
+                table into parts based on partition keys. Supporting diverse
+                partitioning strategies like "identity", "bucket[N]", 
"truncate[W]",
+                 "list", "range", "func", etc.
             - "indexes" represents the table index, such as primary key or 
unique key.
         """
         client = ctx.request_context.lifespan_context.rest_client()
diff --git a/mcp-server/pylintrc b/mcp-server/pylintrc
new file mode 100644
index 0000000000..21bfe53e53
--- /dev/null
+++ b/mcp-server/pylintrc
@@ -0,0 +1,95 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
+confidence=
+
+# Enable the message, report, category or checker with the given id(s).
+# enable=logging
+
+# Disable the message, report, category or checker with the given id(s). 
+disable=missing-class-docstring,
+        missing-function-docstring,
+        missing-module-docstring,
+        global-variable-not-assigned,
+        too-many-public-methods,
+        too-few-public-methods,       
+        unused-argument,
+        no-else-break,
+        no-member,
+        fixme,          
+        unnecessary-pass,               
+        duplicate-code,                 #TODO-fix
+        too-many-arguments,             #TODO-fix
+
+[LOGGING]
+
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
+
+[TYPECHECK]
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=gravitino.utils.http_client.Response
+
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=120
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# maximum number of lines in a file
+max-module-lines=1100
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit.
+indent-string='    '
+
+[BASIC]
+
+# Naming Style for Const
+const-naming-style=UPPER_CASE
+
+# Naming Style for Class
+class-naming-style=PascalCase
+class-const-naming-style=UPPER_CASE
+class-attribute-naming-style=any
+attr-naming-style=snake_case
+
+# Naming Style for Function and Method
+function-naming-style=snake_case
+method-naming-style=snake_case
+argument-naming-style=snake_case
+variable-naming-style=snake_case
+
+# Naming Style for Module
+module-naming-style=snake_case
diff --git a/mcp-server/pyproject.toml b/mcp-server/pyproject.toml
index 5c66261caa..3e204d5527 100644
--- a/mcp-server/pyproject.toml
+++ b/mcp-server/pyproject.toml
@@ -25,6 +25,7 @@ dependencies = [
     "fastmcp>=2.10.6",
     "parameterized>=0.9.0",
     "pytest>=8.4.1",
+    "pylint>=2.20.0",
 ]
 
 [tool.isort]
diff --git a/mcp-server/tests/unit/tools/mock_operation.py 
b/mcp-server/tests/unit/tools/mock_operation.py
index be0fa2120e..27b7bfc030 100644
--- a/mcp-server/tests/unit/tools/mock_operation.py
+++ b/mcp-server/tests/unit/tools/mock_operation.py
@@ -86,6 +86,8 @@ class MockFilesetOperation(FilesetOperation):
     ) -> str:
         return "mock_fileset"
 
+    # pylint: disable=R0917
+    # This method has too many arguments, but it's a mock and we want to keep 
the signature similar to the real one.
     async def list_files_in_fileset(
         self,
         catalog_name: str,
@@ -141,15 +143,15 @@ class MockTagOperation(TagOperation):
         return "mock_tags"
 
     async def create_tag(
-        self, name: str, comment: str, properties: dict
+        self, tag_name: str, tag_comment: str, tag_properties: dict
     ) -> str:
-        return f"mock_tag_created: {name}"
+        return f"mock_tag_created: {tag_name}"
 
-    async def get_tag_by_name(self, name: str) -> str:
-        return f"mock_tag: {name}"
+    async def get_tag_by_name(self, tag_name: str) -> str:
+        return f"mock_tag: {tag_name}"
 
-    async def alter_tag(self, name: str, updates: list) -> str:
-        return f"mock_tag_altered: {name} with updates {updates}"
+    async def alter_tag(self, tag_name: str, updates: list) -> str:
+        return f"mock_tag_altered: {tag_name} with updates {updates}"
 
     async def delete_tag(self, name: str) -> str:
         return f"mock_tag_deleted: {name}"
diff --git a/mcp-server/tests/unit/tools/test_catalog.py 
b/mcp-server/tests/unit/tools/test_catalog.py
index 01a8fd1ef7..ca6c03f735 100644
--- a/mcp-server/tests/unit/tools/test_catalog.py
+++ b/mcp-server/tests/unit/tools/test_catalog.py
@@ -27,13 +27,18 @@ from tests.unit.tools import MockOperation
 
 
 class TestCatalogTool(unittest.TestCase):
+    """Test the catalog tool functionality."""
 
     def setUp(self):
+        """Set up the test environment."""
+
         RESTClientFactory.set_rest_client(MockOperation)
         server = GravitinoMCPServer(Setting("mock_metalake"))
         self.mcp = server.mcp
 
     def test_list_catalogs(self):
+        """Test listing catalogs."""
+
         async def _test_list_catalogs(mcp_server):
             async with Client(mcp_server) as client:
                 result = await client.call_tool("get_list_of_catalogs")
diff --git a/mcp-server/uv.lock b/mcp-server/uv.lock
index b64f8b27b0..0364247693 100644
--- a/mcp-server/uv.lock
+++ b/mcp-server/uv.lock
@@ -1,6 +1,11 @@
 version = 1
-revision = 2
+revision = 3
 requires-python = ">=3.10"
+resolution-markers = [
+    "python_full_version >= '3.12'",
+    "python_full_version == '3.11.*'",
+    "python_full_version < '3.11'",
+]
 
 [[package]]
 name = "annotated-types"
@@ -26,6 +31,18 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl";,
 hash = 
"sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size 
= 100916, upload-time = "2025-03-17T00:02:52.713Z" },
 ]
 
+[[package]]
+name = "astroid"
+version = "3.3.11"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/18/74/dfb75f9ccd592bbedb175d4a32fc643cf569d7c218508bfbd6ea7ef9c091/astroid-3.3.11.tar.gz";,
 hash = 
"sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce", size 
= 400439, upload-time = "2025-07-13T18:04:23.177Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/af/0f/3b8fdc946b4d9cc8cc1e8af42c4e409468c84441b933d037e101b3d72d86/astroid-3.3.11-py3-none-any.whl";,
 hash = 
"sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec", size 
= 275612, upload-time = "2025-07-13T18:04:21.07Z" },
+]
+
 [[package]]
 name = "attrs"
 version = "25.3.0"
@@ -197,6 +214,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/16/1f/4b9f6986add9f6ff361c1bfffeb08fc2f2f6752f8adf8d4dcf0a988b6f28/cyclopts-3.22.3-py3-none-any.whl";,
 hash = 
"sha256:771ae584868c8beeac74184a96e9fad3726c787b17e47a6f0d5f42cece1df57a", size 
= 84941, upload-time = "2025-07-23T23:25:08.527Z" },
 ]
 
+[[package]]
+name = "dill"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz";,
 hash = 
"sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size 
= 186976, upload-time = "2025-04-16T00:41:48.867Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl";,
 hash = 
"sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size 
= 119668, upload-time = "2025-04-16T00:41:47.671Z" },
+]
+
 [[package]]
 name = "dnspython"
 version = "2.7.0"
@@ -277,6 +303,7 @@ source = { virtual = "." }
 dependencies = [
     { name = "fastmcp" },
     { name = "parameterized" },
+    { name = "pylint" },
     { name = "pytest" },
 ]
 
@@ -284,6 +311,7 @@ dependencies = [
 requires-dist = [
     { name = "fastmcp", specifier = ">=2.10.6" },
     { name = "parameterized", specifier = ">=0.9.0" },
+    { name = "pylint", specifier = ">=2.20.0" },
     { name = "pytest", specifier = ">=8.4.1" },
 ]
 
@@ -351,6 +379,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl";,
 hash = 
"sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size 
= 6050, upload-time = "2025-03-19T20:10:01.071Z" },
 ]
 
+[[package]]
+name = "isort"
+version = "6.0.1"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/b8/21/1e2a441f74a653a144224d7d21afe8f4169e6c7c20bb13aec3a2dc3815e0/isort-6.0.1.tar.gz";,
 hash = 
"sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450", size 
= 821955, upload-time = "2025-02-26T21:13:16.955Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/c1/11/114d0a5f4dabbdcedc1125dee0888514c3c3b16d3e9facad87ed96fad97c/isort-6.0.1-py3-none-any.whl";,
 hash = 
"sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615", size 
= 94186, upload-time = "2025-02-26T21:13:14.911Z" },
+]
+
 [[package]]
 name = "jsonschema"
 version = "4.25.0"
@@ -390,6 +427,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl";,
 hash = 
"sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size 
= 87528, upload-time = "2023-06-03T06:41:11.019Z" },
 ]
 
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz";,
 hash = 
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size 
= 9658, upload-time = "2022-01-24T01:14:51.113Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl";,
 hash = 
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size 
= 7350, upload-time = "2022-01-24T01:14:49.62Z" },
+]
+
 [[package]]
 name = "mcp"
 version = "1.12.2"
@@ -451,6 +497,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl";,
 hash = 
"sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size 
= 20475, upload-time = "2023-03-27T02:01:09.31Z" },
 ]
 
+[[package]]
+name = "platformdirs"
+version = "4.3.8"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz";,
 hash = 
"sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size 
= 21362, upload-time = "2025-05-07T22:47:42.121Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl";,
 hash = 
"sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size 
= 18567, upload-time = "2025-05-07T22:47:40.376Z" },
+]
+
 [[package]]
 name = "pluggy"
 version = "1.6.0"
@@ -599,6 +654,25 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl";,
 hash = 
"sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size 
= 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
 ]
 
+[[package]]
+name = "pylint"
+version = "3.3.8"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "astroid" },
+    { name = "colorama", marker = "sys_platform == 'win32'" },
+    { name = "dill" },
+    { name = "isort" },
+    { name = "mccabe" },
+    { name = "platformdirs" },
+    { name = "tomli", marker = "python_full_version < '3.11'" },
+    { name = "tomlkit" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/9d/58/1f614a84d3295c542e9f6e2c764533eea3f318f4592dc1ea06c797114767/pylint-3.3.8.tar.gz";,
 hash = 
"sha256:26698de19941363037e2937d3db9ed94fb3303fdadf7d98847875345a8bb6b05", size 
= 1523947, upload-time = "2025-08-09T09:12:57.234Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/2d/1a/711e93a7ab6c392e349428ea56e794a3902bb4e0284c1997cff2d7efdbc1/pylint-3.3.8-py3-none-any.whl";,
 hash = 
"sha256:7ef94aa692a600e82fabdd17102b73fc226758218c97473c7ad67bd4cb905d83", size 
= 523153, upload-time = "2025-08-09T09:12:54.836Z" },
+]
+
 [[package]]
 name = "pyperclip"
 version = "1.9.0"
@@ -902,6 +976,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl";,
 hash = 
"sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size 
= 14257, upload-time = "2024-11-27T22:38:35.385Z" },
 ]
 
+[[package]]
+name = "tomlkit"
+version = "0.13.3"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz";,
 hash = 
"sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size 
= 185207, upload-time = "2025-06-05T07:13:44.947Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl";,
 hash = 
"sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size 
= 38901, upload-time = "2025-06-05T07:13:43.546Z" },
+]
+
 [[package]]
 name = "typing-extensions"
 version = "4.14.1"


Reply via email to