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

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new ff78f1aab1e Fix ShardingSphere-MCP model-facing contract drift (#39017)
ff78f1aab1e is described below

commit ff78f1aab1e884a098c6173cc37aaabde8fdfeac
Author: Liang Zhang <[email protected]>
AuthorDate: Mon Jul 6 11:56:01 2026 +0800

    Fix ShardingSphere-MCP model-facing contract drift (#39017)
    
    * Fix ShardingSphere-MCP model-facing contract drift
    
    - complete storageUnit URI variables from governance metadata
    - add planning tool annotation titles for sharding and shadow
    - align protocol docs and registry description with the public MCP surface
    
    * Fix ShardingSphere-MCP model-facing contract drift
    
    - complete storageUnit URI variables from governance metadata
    - add planning tool annotation titles for sharding and shadow
    - align protocol docs and registry description with the public MCP surface
---
 .../content/reference/mcp/protocol-surface.cn.md   |  4 +-
 .../content/reference/mcp/protocol-surface.en.md   |  4 +-
 .../provider/MetadataCompletionProvider.java       | 28 ++++++++++--
 .../core/handler/core/CoreResourceHandlers.java    |  2 +-
 .../metadata/GovernanceMetadataQueryService.java   |  2 +-
 .../handler/metadata/MetadataSearchCollector.java  |  2 +-
 .../metadata/SearchMetadataToolService.java        |  2 +-
 .../provider/MetadataCompletionProviderTest.java   | 51 ++++++++++++++++++++++
 .../GovernanceMetadataQueryServiceTest.java        |  2 +-
 .../mcp-descriptors/mcp-descriptor-shadow.yaml     |  3 ++
 .../shadow/ShadowDescriptorContractTest.java       | 47 ++++++++++++++++++++
 .../mcp-descriptors/mcp-descriptor-sharding.yaml   | 38 +++++++++++++---
 .../sharding/ShardingDescriptorContractTest.java   | 50 +++++++++++++++++++++
 mcp/server.json                                    |  2 +-
 .../model-contract/capabilities.yaml               | 36 +++++++--------
 .../baseline-contract/model-contract/guidance.yaml | 16 +++++++
 16 files changed, 252 insertions(+), 37 deletions(-)

diff --git a/docs/document/content/reference/mcp/protocol-surface.cn.md 
b/docs/document/content/reference/mcp/protocol-surface.cn.md
index f2a3ff34f64..7572cc2f70a 100644
--- a/docs/document/content/reference/mcp/protocol-surface.cn.md
+++ b/docs/document/content/reference/mcp/protocol-surface.cn.md
@@ -40,7 +40,7 @@ ShardingSphere-MCP 不要求 roots,也不会发送 `sampling/createMessage` 
 
 - 搜索逻辑库 metadata。
 - 可按 `database`、`schema`、`query`、`object_types` 收窄范围。
-- `object_types` 支持 
`database`、`schema`、`table`、`view`、`column`、`index`、`sequence`。
+- `object_types` 支持 
`database`、`schema`、`storage_unit`、`table`、`view`、`column`、`index`、`sequence`。
 
 `database_gateway_validate_runtime_database`
 
@@ -200,7 +200,7 @@ Feature resources:
 ## Completions
 
 Completions 用于补全运行时名称、metadata identifier、算法和当前 session 中的 workflow `plan_id`。
-Client 应在选择不确定的 database、schema、table、column、algorithm 或 `plan_id` 前调用 
`completion/complete`,或读取最近的 MCP resource。
+Client 应在选择不确定的 database、schema、table、column、storage unit、algorithm 或 
`plan_id` 前调用 `completion/complete`,或读取最近的 MCP resource。
 当 completion 响应包含 meta `next_actions` 时,client 应先遵循这些动作,再猜测取值或切换到其他 tool。
 重试 completion 前,可通过 `resources/templates/list` 发现最近 resource 所需的 URI 变量,并补齐上下文。
 
diff --git a/docs/document/content/reference/mcp/protocol-surface.en.md 
b/docs/document/content/reference/mcp/protocol-surface.en.md
index 5a581e75b31..fcf02622ca9 100644
--- a/docs/document/content/reference/mcp/protocol-surface.en.md
+++ b/docs/document/content/reference/mcp/protocol-surface.en.md
@@ -40,7 +40,7 @@ ShardingSphere-MCP does not require roots and does not send 
`sampling/createMess
 
 - Searches logical database metadata.
 - Narrows scope by `database`, `schema`, `query`, and `object_types`.
-- `object_types` supports `database`, `schema`, `table`, `view`, `column`, 
`index`, and `sequence`.
+- `object_types` supports `database`, `schema`, `storage_unit`, `table`, 
`view`, `column`, `index`, and `sequence`.
 
 `database_gateway_validate_runtime_database`
 
@@ -200,7 +200,7 @@ Feature resources:
 ## Completions
 
 Completions suggest runtime names, metadata identifiers, algorithms, and 
workflow `plan_id` values in the current session.
-Before choosing uncertain database, schema, table, column, algorithm, or 
`plan_id` values, clients should call `completion/complete` or read the nearest 
MCP resource.
+Before choosing uncertain database, schema, table, column, storage unit, 
algorithm, or `plan_id` values, clients should call `completion/complete` or 
read the nearest MCP resource.
 When a completion response includes meta `next_actions`, clients should follow 
those actions before guessing a value or switching to another tool.
 Use `resources/templates/list` to discover URI variables for the nearest 
resource before retrying completion with additional context.
 
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProvider.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProvider.java
index 8b7dbbde614..cd3961d6a5c 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProvider.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProvider.java
@@ -18,6 +18,7 @@
 package org.apache.shardingsphere.mcp.core.completion.provider;
 
 import 
org.apache.shardingsphere.mcp.api.protocol.exception.MCPUnsupportedException;
+import 
org.apache.shardingsphere.mcp.core.metadata.GovernanceMetadataQueryService;
 import org.apache.shardingsphere.mcp.support.completion.MCPCompletionCandidate;
 import org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider;
 import 
org.apache.shardingsphere.mcp.support.completion.MCPCompletionProviderResult;
@@ -29,6 +30,7 @@ import 
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPIndexMet
 import 
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPSequenceMetadata;
 import 
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPSchemaMetadata;
 import 
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPTableMetadata;
+import 
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
 import org.apache.shardingsphere.mcp.support.resource.MCPUriPathSegmentUtils;
 
 import java.util.Collection;
@@ -45,7 +47,9 @@ import java.util.stream.Stream;
  */
 public final class MetadataCompletionProvider implements 
MCPCompletionProvider<MCPDatabaseHandlerContext> {
     
-    private static final Set<String> SUPPORTED_ARGUMENTS = Set.of("database", 
"schema", "table", "column", "index", "sequence");
+    private static final Set<String> SUPPORTED_ARGUMENTS = Set.of("database", 
"schema", "table", "column", "index", "sequence", "storageUnit");
+    
+    private final GovernanceMetadataQueryService 
governanceMetadataQueryService = new GovernanceMetadataQueryService();
     
     @Override
     public Class<MCPDatabaseHandlerContext> getContextType() {
@@ -109,7 +113,7 @@ public final class MetadataCompletionProvider implements 
MCPCompletionProvider<M
     }
     
     private boolean requiresDatabaseContext(final String argumentName) {
-        return "schema".equals(argumentName) || 
requiresSchemaContext(argumentName);
+        return "schema".equals(argumentName) || 
"storageUnit".equals(argumentName) || requiresSchemaContext(argumentName);
     }
     
     private boolean requiresSchemaContext(final String argumentName) {
@@ -132,6 +136,9 @@ public final class MetadataCompletionProvider implements 
MCPCompletionProvider<M
         if ("index".equals(argumentName)) {
             return completeIndexes(handlerContext, contextArguments);
         }
+        if ("storageUnit".equals(argumentName)) {
+            return completeStorageUnits(handlerContext, contextArguments);
+        }
         return "sequence".equals(argumentName) ? 
completeSequences(handlerContext, contextArguments) : List.of();
     }
     
@@ -193,12 +200,24 @@ public final class MetadataCompletionProvider implements 
MCPCompletionProvider<M
         }
     }
     
+    private List<MCPCompletionCandidate> completeStorageUnits(final 
MCPDatabaseHandlerContext handlerContext, final Map<String, String> 
contextArguments) {
+        String database = contextArguments.getOrDefault("database", "");
+        if (database.isEmpty()) {
+            return List.of();
+        }
+        MCPFeatureQueryFacade queryFacade = handlerContext.getQueryFacade();
+        return governanceMetadataQueryService.queryStorageUnits(queryFacade, 
database).stream()
+                .map(each -> Objects.toString(each.get("name"), ""))
+                .filter(each -> !each.isEmpty())
+                .map(each -> new MCPCompletionCandidate(each, "storage unit", 
"metadata")).toList();
+    }
+    
     private String getSchema(final Map<String, String> contextArguments) {
         return Objects.toString(contextArguments.get("schema"), "");
     }
     
     private List<String> createMissingContextArguments(final String 
argumentName, final Map<String, String> contextArguments) {
-        if ("schema".equals(argumentName)) {
+        if ("schema".equals(argumentName) || 
"storageUnit".equals(argumentName)) {
             return createMissingArguments(contextArguments, "database");
         }
         if ("table".equals(argumentName) || "sequence".equals(argumentName)) {
@@ -229,6 +248,9 @@ public final class MetadataCompletionProvider implements 
MCPCompletionProvider<M
         if ("sequence".equals(argumentName) && !database.isEmpty() && 
!schema.isEmpty()) {
             return 
String.format("shardingsphere://databases/%s/schemas/%s/sequences", 
encode(database), encode(schema));
         }
+        if ("storageUnit".equals(argumentName) && !database.isEmpty()) {
+            return 
String.format("shardingsphere://databases/%s/storage-units", encode(database));
+        }
         String table = Objects.toString(contextArguments.get("table"), "");
         if ("column".equals(argumentName) && !database.isEmpty() && 
!schema.isEmpty() && !table.isEmpty()) {
             return 
String.format("shardingsphere://databases/%s/schemas/%s/tables/%s/columns", 
encode(database), encode(schema), encode(table));
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreResourceHandlers.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreResourceHandlers.java
index e899d659585..9ed94a2ac67 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreResourceHandlers.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreResourceHandlers.java
@@ -26,7 +26,7 @@ import 
org.apache.shardingsphere.mcp.core.resource.handler.capability.DatabaseCa
 import 
org.apache.shardingsphere.mcp.core.resource.handler.capability.RuntimeStatusHandler;
 import 
org.apache.shardingsphere.mcp.core.resource.handler.capability.ServerCapabilitiesHandler;
 import 
org.apache.shardingsphere.mcp.core.resource.handler.capability.ServerGuidanceHandler;
-import 
org.apache.shardingsphere.mcp.core.resource.handler.metadata.GovernanceMetadataQueryService;
+import 
org.apache.shardingsphere.mcp.core.metadata.GovernanceMetadataQueryService;
 import 
org.apache.shardingsphere.mcp.core.resource.handler.metadata.MetadataResourceHandler;
 import 
org.apache.shardingsphere.mcp.core.resource.handler.workflow.WorkflowPlanHandler;
 
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/GovernanceMetadataQueryService.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/metadata/GovernanceMetadataQueryService.java
similarity index 99%
rename from 
mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/GovernanceMetadataQueryService.java
rename to 
mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/metadata/GovernanceMetadataQueryService.java
index 1c0a2886c28..ae039357150 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/GovernanceMetadataQueryService.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/metadata/GovernanceMetadataQueryService.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.shardingsphere.mcp.core.resource.handler.metadata;
+package org.apache.shardingsphere.mcp.core.metadata;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/MetadataSearchCollector.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/MetadataSearchCollector.java
index 85480ca5d20..3b5d1420204 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/MetadataSearchCollector.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/MetadataSearchCollector.java
@@ -19,7 +19,7 @@ package 
org.apache.shardingsphere.mcp.core.tool.handler.metadata;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
-import 
org.apache.shardingsphere.mcp.core.resource.handler.metadata.GovernanceMetadataQueryService;
+import 
org.apache.shardingsphere.mcp.core.metadata.GovernanceMetadataQueryService;
 import org.apache.shardingsphere.mcp.core.tool.request.MetadataSearchRequest;
 import org.apache.shardingsphere.mcp.core.tool.response.MetadataSearchHit;
 import 
org.apache.shardingsphere.mcp.support.database.capability.SupportedMCPMetadataObjectType;
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolService.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolService.java
index 9543fe9e90a..69a68644891 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolService.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolService.java
@@ -22,7 +22,7 @@ import 
org.apache.shardingsphere.mcp.api.protocol.exception.MCPInvalidRequestExc
 import org.apache.shardingsphere.mcp.core.tool.request.MetadataSearchRequest;
 import org.apache.shardingsphere.mcp.core.tool.response.MetadataSearchHit;
 import org.apache.shardingsphere.mcp.core.tool.response.MetadataSearchResult;
-import 
org.apache.shardingsphere.mcp.core.resource.handler.metadata.GovernanceMetadataQueryService;
+import 
org.apache.shardingsphere.mcp.core.metadata.GovernanceMetadataQueryService;
 import 
org.apache.shardingsphere.mcp.support.database.capability.SupportedMCPMetadataObjectType;
 import 
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
 import 
org.apache.shardingsphere.mcp.support.database.spi.MCPMetadataQueryFacade;
diff --git 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProviderTest.java
 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProviderTest.java
index 1a664ac5e39..d1ec00cc8be 100644
--- 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProviderTest.java
+++ 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProviderTest.java
@@ -29,6 +29,7 @@ import 
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPSequence
 import 
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPSchemaMetadata;
 import 
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPTableMetadata;
 import 
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureCapabilityFacade;
+import 
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
 import 
org.apache.shardingsphere.mcp.support.database.spi.MCPMetadataQueryFacade;
 import 
org.apache.shardingsphere.mcp.support.descriptor.MCPCompletionTargetDescriptor;
 import org.junit.jupiter.api.Test;
@@ -60,6 +61,11 @@ class MetadataCompletionProviderTest {
         assertTrue(new 
MetadataCompletionProvider().supports(createRequestContext("database", 
Map.of())));
     }
     
+    @Test
+    void assertSupportsStorageUnit() {
+        assertTrue(new 
MetadataCompletionProvider().supports(createRequestContext("storageUnit", 
Map.of())));
+    }
+    
     @Test
     void assertSupportsWithUnknownArgument() {
         assertFalse(new 
MetadataCompletionProvider().supports(createRequestContext("foo_value", 
Map.of())));
@@ -169,6 +175,38 @@ class MetadataCompletionProviderTest {
         assertThat(actual.getNearestResourceUri(), 
is("shardingsphere://databases/logic_db/schemas/public/sequences"));
     }
     
+    @Test
+    void assertCompleteStorageUnit() {
+        MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+        when(queryFacade.query("logic_db", "", "SHOW STORAGE UNITS FROM 
logic_db")).thenReturn(List.of(Map.of("name", "write_ds")));
+        MCPCompletionProviderResult actual = new 
MetadataCompletionProvider().complete(createHandlerContext(mock(MCPMetadataQueryFacade.class),
 queryFacade),
+                createRequestContext("storageUnit", Map.of("database", 
"logic_db")));
+        assertCandidate(actual, "write_ds");
+        assertThat(actual.getMissingContextArguments(), is(List.of()));
+        assertThat(actual.getNearestResourceUri(), 
is("shardingsphere://databases/logic_db/storage-units"));
+    }
+    
+    @Test
+    void assertCompleteStorageUnitWithSingleDatabaseDefaulted() {
+        MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+        when(queryFacade.query("logic_db", "", "SHOW STORAGE UNITS FROM 
logic_db")).thenReturn(List.of(Map.of("name", "write_ds")));
+        MCPCompletionProviderResult actual = new 
MetadataCompletionProvider().complete(
+                createHandlerContext(mock(MCPMetadataQueryFacade.class), 
queryFacade, List.of(createDatabaseProfile("logic_db"))), 
createRequestContext("storageUnit", Map.of()));
+        assertCandidate(actual, "write_ds");
+        assertThat(actual.getInferredContextArguments(), is(Map.of("database", 
"logic_db")));
+        assertThat(actual.getMissingContextArguments(), is(List.of()));
+        assertThat(actual.getNearestResourceUri(), 
is("shardingsphere://databases/logic_db/storage-units"));
+    }
+    
+    @Test
+    void assertCompleteStorageUnitWithMissingContext() {
+        MCPCompletionProviderResult actual = new 
MetadataCompletionProvider().complete(createHandlerContext(mock(MCPMetadataQueryFacade.class),
+                List.of(createDatabaseProfile("logic_db"), 
createDatabaseProfile("warehouse"))), createRequestContext("storageUnit", 
Map.of()));
+        assertThat(actual.getCandidates(), is(List.of()));
+        assertThat(actual.getMissingContextArguments(), 
is(List.of("database")));
+        assertThat(actual.getNearestResourceUri(), 
is("shardingsphere://databases"));
+    }
+    
     private MCPCompletionRequestContext createRequestContext(final String 
argumentName, final Map<String, String> contextArguments) {
         return new MCPCompletionRequestContext("session-1", new 
MCPCompletionTargetDescriptor("prompt", "inspect_metadata", 
List.of(argumentName), 50, Map.of()), argumentName,
                 contextArguments);
@@ -187,6 +225,19 @@ class MetadataCompletionProviderTest {
         return result;
     }
     
+    private MCPDatabaseHandlerContext createHandlerContext(final 
MCPMetadataQueryFacade metadataQueryFacade, final MCPFeatureQueryFacade 
queryFacade) {
+        MCPDatabaseHandlerContext result = 
createHandlerContext(metadataQueryFacade, List.of());
+        when(result.getQueryFacade()).thenReturn(queryFacade);
+        return result;
+    }
+    
+    private MCPDatabaseHandlerContext createHandlerContext(final 
MCPMetadataQueryFacade metadataQueryFacade, final MCPFeatureQueryFacade 
queryFacade,
+                                                           final 
List<RuntimeDatabaseProfile> databaseProfiles) {
+        MCPDatabaseHandlerContext result = 
createHandlerContext(metadataQueryFacade, databaseProfiles);
+        when(result.getQueryFacade()).thenReturn(queryFacade);
+        return result;
+    }
+    
     private RuntimeDatabaseProfile createDatabaseProfile(final String 
database) {
         return new RuntimeDatabaseProfile(database, "MySQL", "8.0");
     }
diff --git 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/GovernanceMetadataQueryServiceTest.java
 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/metadata/GovernanceMetadataQueryServiceTest.java
similarity index 98%
rename from 
mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/GovernanceMetadataQueryServiceTest.java
rename to 
mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/metadata/GovernanceMetadataQueryServiceTest.java
index 635f3200a7f..bf0a089b1e8 100644
--- 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/GovernanceMetadataQueryServiceTest.java
+++ 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/metadata/GovernanceMetadataQueryServiceTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.shardingsphere.mcp.core.resource.handler.metadata;
+package org.apache.shardingsphere.mcp.core.metadata;
 
 import 
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
 import org.junit.jupiter.api.Test;
diff --git 
a/mcp/features/shadow/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-shadow.yaml
 
b/mcp/features/shadow/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-shadow.yaml
index 90631af4d88..1448274f8e5 100644
--- 
a/mcp/features/shadow/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-shadow.yaml
+++ 
b/mcp/features/shadow/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-shadow.yaml
@@ -593,6 +593,7 @@ tools:
           proxy_topology_hint: {}
           next_actions: []
     annotations:
+      title: Plan Shadow Rule
       destructiveHint: false
       idempotentHint: false
       openWorldHint: false
@@ -673,6 +674,7 @@ tools:
           proxy_topology_hint: {}
           next_actions: []
     annotations:
+      title: Plan Default Shadow Algorithm
       destructiveHint: false
       idempotentHint: false
       openWorldHint: false
@@ -743,6 +745,7 @@ tools:
           proxy_topology_hint: {}
           next_actions: []
     annotations:
+      title: Plan Shadow Algorithm Cleanup
       destructiveHint: false
       idempotentHint: false
       openWorldHint: false
diff --git 
a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/ShadowDescriptorContractTest.java
 
b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/ShadowDescriptorContractTest.java
new file mode 100644
index 00000000000..dfe1bec39d7
--- /dev/null
+++ 
b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/ShadowDescriptorContractTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package org.apache.shardingsphere.mcp.feature.shadow;
+
+import org.apache.shardingsphere.mcp.api.tool.descriptor.MCPToolDescriptor;
+import org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalog;
+import 
org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalogLoader;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+class ShadowDescriptorContractTest {
+    
+    @Test
+    void assertPlanningToolAnnotationTitles() {
+        MCPDescriptorCatalog catalog = MCPDescriptorCatalogLoader.load();
+        for (Entry<String, String> entry : Map.of(
+                ShadowFeatureDefinition.PLAN_RULE_TOOL_NAME, "Plan Shadow 
Rule",
+                ShadowFeatureDefinition.PLAN_DEFAULT_ALGORITHM_TOOL_NAME, 
"Plan Default Shadow Algorithm",
+                ShadowFeatureDefinition.PLAN_ALGORITHM_CLEANUP_TOOL_NAME, 
"Plan Shadow Algorithm Cleanup").entrySet()) {
+            assertThat(findTool(catalog, 
entry.getKey()).getAnnotations().getTitle(), is(entry.getValue()));
+        }
+    }
+    
+    private MCPToolDescriptor findTool(final MCPDescriptorCatalog catalog, 
final String toolName) {
+        return 
catalog.getProtocolDescriptors().getToolDescriptors().stream().filter(each -> 
toolName.equals(each.getName())).findFirst().orElseThrow();
+    }
+}
diff --git 
a/mcp/features/sharding/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-sharding.yaml
 
b/mcp/features/sharding/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-sharding.yaml
index 28a0612319f..05a98b08685 100644
--- 
a/mcp/features/sharding/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-sharding.yaml
+++ 
b/mcp/features/sharding/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-sharding.yaml
@@ -850,7 +850,8 @@ tools:
           missing_required_inputs: []
           resources_to_read: []
           next_actions: []
-    annotations: &planningAnnotations
+    annotations:
+      title: Plan Sharding Table Rule
       destructiveHint: false
       idempotentHint: false
       openWorldHint: false
@@ -890,7 +891,12 @@ tools:
       required: []
       additionalProperties: false
     outputSchema: *workflowPlanOutputSchema
-    annotations: *planningAnnotations
+    annotations:
+      title: Plan Sharding Table Reference Rule
+      destructiveHint: false
+      idempotentHint: false
+      openWorldHint: false
+      readOnlyHint: false
     runtime: *planningRuntime
     meta:
       org.apache.shardingsphere/workflow-kind: sharding.table.reference
@@ -926,7 +932,12 @@ tools:
       required: []
       additionalProperties: false
     outputSchema: *workflowPlanOutputSchema
-    annotations: *planningAnnotations
+    annotations:
+      title: Plan Sharding Default Strategy
+      destructiveHint: false
+      idempotentHint: false
+      openWorldHint: false
+      readOnlyHint: false
     runtime: *planningRuntime
     meta:
       org.apache.shardingsphere/workflow-kind: sharding.default.strategy
@@ -960,7 +971,12 @@ tools:
       required: []
       additionalProperties: false
     outputSchema: *workflowPlanOutputSchema
-    annotations: *planningAnnotations
+    annotations:
+      title: Plan Sharding Key Generator
+      destructiveHint: false
+      idempotentHint: false
+      openWorldHint: false
+      readOnlyHint: false
     runtime: *planningRuntime
     meta:
       org.apache.shardingsphere/workflow-kind: sharding.key.generator
@@ -998,7 +1014,12 @@ tools:
       required: []
       additionalProperties: false
     outputSchema: *workflowPlanOutputSchema
-    annotations: *planningAnnotations
+    annotations:
+      title: Plan Sharding Key Generate Strategy
+      destructiveHint: false
+      idempotentHint: false
+      openWorldHint: false
+      readOnlyHint: false
     runtime: *planningRuntime
     meta:
       org.apache.shardingsphere/workflow-kind: sharding.key.generate.strategy
@@ -1032,7 +1053,12 @@ tools:
       required: []
       additionalProperties: false
     outputSchema: *workflowPlanOutputSchema
-    annotations: *planningAnnotations
+    annotations:
+      title: Plan Sharding Rule Component Cleanup
+      destructiveHint: false
+      idempotentHint: false
+      openWorldHint: false
+      readOnlyHint: false
     runtime: *planningRuntime
     meta:
       org.apache.shardingsphere/workflow-kind: sharding.component.cleanup
diff --git 
a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingDescriptorContractTest.java
 
b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingDescriptorContractTest.java
new file mode 100644
index 00000000000..1d3d30da93d
--- /dev/null
+++ 
b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingDescriptorContractTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package org.apache.shardingsphere.mcp.feature.sharding;
+
+import org.apache.shardingsphere.mcp.api.tool.descriptor.MCPToolDescriptor;
+import org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalog;
+import 
org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalogLoader;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+class ShardingDescriptorContractTest {
+    
+    @Test
+    void assertPlanningToolAnnotationTitles() {
+        MCPDescriptorCatalog catalog = MCPDescriptorCatalogLoader.load();
+        for (Entry<String, String> entry : Map.of(
+                ShardingFeatureDefinition.PLAN_TABLE_RULE_TOOL_NAME, "Plan 
Sharding Table Rule",
+                ShardingFeatureDefinition.PLAN_TABLE_REFERENCE_TOOL_NAME, 
"Plan Sharding Table Reference Rule",
+                ShardingFeatureDefinition.PLAN_DEFAULT_STRATEGY_TOOL_NAME, 
"Plan Sharding Default Strategy",
+                ShardingFeatureDefinition.PLAN_KEY_GENERATOR_TOOL_NAME, "Plan 
Sharding Key Generator",
+                
ShardingFeatureDefinition.PLAN_KEY_GENERATE_STRATEGY_TOOL_NAME, "Plan Sharding 
Key Generate Strategy",
+                ShardingFeatureDefinition.PLAN_COMPONENT_CLEANUP_TOOL_NAME, 
"Plan Sharding Rule Component Cleanup").entrySet()) {
+            assertThat(findTool(catalog, 
entry.getKey()).getAnnotations().getTitle(), is(entry.getValue()));
+        }
+    }
+    
+    private MCPToolDescriptor findTool(final MCPDescriptorCatalog catalog, 
final String toolName) {
+        return 
catalog.getProtocolDescriptors().getToolDescriptors().stream().filter(each -> 
toolName.equals(each.getName())).findFirst().orElseThrow();
+    }
+}
diff --git a/mcp/server.json b/mcp/server.json
index 3680276dd8e..cd461022b33 100644
--- a/mcp/server.json
+++ b/mcp/server.json
@@ -2,7 +2,7 @@
   "$schema": 
"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json";,
   "name": "io.github.apache/shardingsphere-mcp",
   "title": "Apache ShardingSphere MCP",
-  "description": "Apache ShardingSphere MCP Server for metadata discovery, SQL 
preview, and encrypt/mask workflows",
+  "description": "Apache ShardingSphere MCP Server for metadata discovery, 
controlled SQL, and rule workflows",
   "websiteUrl": 
"https://shardingsphere.apache.org/document/current/en/user-manual/shardingsphere-mcp/";,
   "repository": {
     "url": "https://github.com/apache/shardingsphere";,
diff --git 
a/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/capabilities.yaml
 
b/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/capabilities.yaml
index 41a86f28a61..480bf02cd52 100644
--- 
a/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/capabilities.yaml
+++ 
b/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/capabilities.yaml
@@ -590,32 +590,32 @@ tools:
   annotations: {title: Plan Readwrite-Splitting Status, readOnlyHint: false, 
destructiveHint: false,
     idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_shadow_rule
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Shadow Rule, readOnlyHint: false, destructiveHint: 
false,
+    idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_default_shadow_algorithm
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Default Shadow Algorithm, readOnlyHint: false, 
destructiveHint: false,
+    idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_shadow_algorithm_cleanup
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Shadow Algorithm Cleanup, readOnlyHint: false, 
destructiveHint: false,
+    idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_sharding_table_rule
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Sharding Table Rule, readOnlyHint: false, 
destructiveHint: false,
+    idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_sharding_table_reference_rule
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Sharding Table Reference Rule, readOnlyHint: 
false, destructiveHint: false,
+    idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_sharding_default_strategy
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Sharding Default Strategy, readOnlyHint: false, 
destructiveHint: false,
+    idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_sharding_key_generator
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Sharding Key Generator, readOnlyHint: false, 
destructiveHint: false,
+    idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_sharding_key_generate_strategy
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Sharding Key Generate Strategy, readOnlyHint: 
false,
+    destructiveHint: false, idempotentHint: false, openWorldHint: false}
 - name: database_gateway_plan_sharding_rule_component_cleanup
-  annotations: {readOnlyHint: false, destructiveHint: false, idempotentHint: 
false,
-    openWorldHint: false}
+  annotations: {title: Plan Sharding Rule Component Cleanup, readOnlyHint: 
false,
+    destructiveHint: false, idempotentHint: false, openWorldHint: false}
 - name: database_gateway_apply_workflow
   annotations: {title: Apply Workflow, readOnlyHint: false, destructiveHint: 
true,
     idempotentHint: false, openWorldHint: true}
diff --git 
a/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/guidance.yaml
 
b/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/guidance.yaml
index b227826ebaa..64f4eeec3ab 100644
--- 
a/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/guidance.yaml
+++ 
b/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/guidance.yaml
@@ -38,6 +38,10 @@ model_first_summary:
       execution_mode=preview, next_step: call_tool 
database_gateway_execute_update
       execution_mode=execute, stop_rule: Execute only after preview review 
confirms
       the intended side effect.}
+  - {intent: complete_uncertain_argument, first_action: call 
completion/complete for
+      one uncertain argument, next_step: follow completion meta.next_actions 
when
+      context is missing or no candidates match, stop_rule: Stop after the 
argument
+      is selected or the nearest resource proves it is unavailable.}
   - {intent: recover_error, first_action: follow top-level next_actions, 
next_step: fallback
       to recovery.next_actions when top-level actions are absent, stop_rule: 
'Ask the
       user only when no deterministic resource, completion, or tool action is 
available.'}
@@ -87,6 +91,10 @@ model_contract:
     tool; re-plan when the plan is unavailable.
   side_effect_rule: Preview before side effects and continue only when the 
requested
     side effect is still intended.
+  completion_rule: Use completion/complete for one uncertain argument at a 
time; when
+    completion reports missing context, follow meta.next_actions before 
guessing.
+  resource_template_rule: Use resources/templates/list to discover URI 
variables,
+    then read the nearest concrete resource before filling dependent 
completion context.
   next_action_rule: 'Use canonical next_actions fields: type, tool_name, 
resource_uri,
     and arguments.'
   detail_resource_rule: Use resource descriptors, outputSchema, and returned 
payload
@@ -165,6 +173,14 @@ common_flows:
   stop_condition: Execute only after reviewing the previewed SQL and 
side-effect scope.
   referenced_tools: [database_gateway_execute_update]
   referenced_resources: &id001 []
+- flow_id: complete_uncertain_argument
+  steps: [resources/templates/list, call completion/complete for one argument, 
'follow
+      completion meta.next_actions when diagnostic is missing_context, 
prefix_filtered_all_candidates,
+      or no_candidates']
+  stop_condition: Stop when a completion value is selected or the nearest 
resource
+    proves the argument is unavailable.
+  referenced_tools: *id001
+  referenced_resources: *id001
 - flow_id: workflow_plan_apply_validate
   steps: [call_tool descriptor-backed feature planning tool, call_tool 
database_gateway_apply_workflow
       execution_mode=preview, call_tool database_gateway_apply_workflow 
execution_mode=review-then-execute


Reply via email to