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 d4d615e3de4 Refine MCP workflow plan completion (#39021)
d4d615e3de4 is described below

commit d4d615e3de4702ff04be250eb5b6590b10793a0a
Author: Liang Zhang <[email protected]>
AuthorDate: Mon Jul 6 18:50:00 2026 +0800

    Refine MCP workflow plan completion (#39021)
    
    - scope plan_id completion by prompt workflow kind
    - align planning descriptor next_actions with apply workflow preview
    - tighten feature completion argument contract tests
---
 .../MCPCompletionSpecificationFactoryTest.java     | 22 ++++++++++-
 .../provider/WorkflowPlanIdCompletionProvider.java | 15 +++++--
 .../broadcast/BroadcastFeatureDefinitionTest.java  |  3 +-
 .../mcp-descriptors/mcp-descriptor-encrypt.yaml    |  4 +-
 .../encrypt/EncryptDescriptorContractTest.java     |  7 +++-
 .../mcp-descriptors/mcp-descriptor-mask.yaml       |  4 +-
 .../feature/mask/MaskFeatureDefinitionTest.java    |  3 +-
 .../ReadwriteSplittingFeatureDefinitionTest.java   |  5 +--
 .../mcp-descriptors/mcp-descriptor-shadow.yaml     | 30 ++++++++++++--
 .../shadow/ShadowDescriptorContractTest.java       |  5 +--
 .../mcp-descriptors/mcp-descriptor-sharding.yaml   | 10 ++++-
 .../sharding/ShardingDescriptorContractTest.java   |  3 +-
 .../descriptor/MCPDescriptorCatalogIndex.java      | 46 ++++++++++++++++++++++
 .../descriptor/MCPDescriptorCatalogIndexTest.java  | 12 ++++++
 .../mcp-descriptor-test-planning.yaml              |  4 +-
 15 files changed, 148 insertions(+), 25 deletions(-)

diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/completion/MCPCompletionSpecificationFactoryTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/completion/MCPCompletionSpecificationFactoryTest.java
index 5434876f066..636a3b9f6f7 100644
--- 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/completion/MCPCompletionSpecificationFactoryTest.java
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/completion/MCPCompletionSpecificationFactoryTest.java
@@ -28,6 +28,7 @@ import 
org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatab
 import 
org.apache.shardingsphere.mcp.support.descriptor.MCPShardingSphereMetadataKeys;
 import org.apache.shardingsphere.mcp.support.workflow.WorkflowSessionContext;
 import 
org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowKind;
 import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowLifecycle;
 import org.junit.jupiter.api.Test;
 
@@ -151,6 +152,19 @@ class MCPCompletionSpecificationFactoryTest {
         assertThat(((Map<?, ?>) ((List<?>) 
actual.meta().get(MCPShardingSphereMetadataKeys.VALUE_DETAILS)).getFirst()).get("rankingReason"),
 is("recent-plan-first-for-plan_id"));
     }
     
+    @Test
+    void assertCompletePlanIdsByPromptWorkflowKind() {
+        WorkflowSessionContext workflowSessionContext = new 
InMemoryWorkflowSessionContext();
+        workflowSessionContext.save(createSnapshot("encrypt-plan", 
"session-1", "encrypt.rule", WorkflowLifecycle.STATUS_PLANNED));
+        workflowSessionContext.save(createSnapshot("mask-plan", "session-1", 
"mask.rule", WorkflowLifecycle.STATUS_PLANNED));
+        SyncCompletionSpecification completionSpecification = 
findCompletion(createFactory(createRuntimeContext(workflowSessionContext)).createCompletionSpecifications(),
+                new McpSchema.PromptReference("plan_encrypt_rule"));
+        McpSchema.CompleteResult actual = 
completionSpecification.completionHandler().apply(createExchange("session-1"),
+                new McpSchema.CompleteRequest(new 
McpSchema.PromptReference("plan_encrypt_rule"), new 
McpSchema.CompleteRequest.CompleteArgument("plan_id", "")));
+        assertThat(actual.completion().values(), is(List.of("encrypt-plan")));
+        assertThat(actual.completion().total(), is(1));
+    }
+    
     @Test
     void assertCompletePlanIdsWithExactMatchFirst() {
         WorkflowSessionContext workflowSessionContext = 
mock(WorkflowSessionContext.class);
@@ -200,7 +214,13 @@ class MCPCompletionSpecificationFactoryTest {
     }
     
     private WorkflowContextSnapshot createSnapshot(final String planId, final 
String sessionId, final String status) {
-        return createSnapshot(planId, sessionId, status, null);
+        return createSnapshot(planId, sessionId, status, (Instant) null);
+    }
+    
+    private WorkflowContextSnapshot createSnapshot(final String planId, final 
String sessionId, final String workflowKind, final String status) {
+        WorkflowContextSnapshot result = createSnapshot(planId, sessionId, 
status);
+        result.setWorkflowKind(WorkflowKind.valueOf(workflowKind));
+        return result;
     }
     
     private WorkflowContextSnapshot createSnapshot(final String planId, final 
String sessionId, final String status, final Instant updateTime) {
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/provider/WorkflowPlanIdCompletionProvider.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/provider/WorkflowPlanIdCompletionProvider.java
index 3eb624f73ba..7c3aec667cc 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/provider/WorkflowPlanIdCompletionProvider.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/completion/provider/WorkflowPlanIdCompletionProvider.java
@@ -21,11 +21,13 @@ import 
org.apache.shardingsphere.mcp.support.completion.MCPCompletionCandidate;
 import org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider;
 import 
org.apache.shardingsphere.mcp.support.completion.MCPCompletionProviderResult;
 import 
org.apache.shardingsphere.mcp.support.completion.MCPCompletionRequestContext;
+import 
org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalogIndex;
 import 
org.apache.shardingsphere.mcp.support.workflow.MCPWorkflowHandlerContext;
 import 
org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
 import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
 import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowLifecycle;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -50,11 +52,14 @@ public final class WorkflowPlanIdCompletionProvider 
implements MCPCompletionProv
     
     @Override
     public MCPCompletionProviderResult complete(final 
MCPWorkflowHandlerContext handlerContext, final MCPCompletionRequestContext 
requestContext) {
-        return new MCPCompletionProviderResult(completePlanIds(handlerContext, 
requestContext.getSessionId()));
+        return new MCPCompletionProviderResult(completePlanIds(handlerContext, 
requestContext));
     }
     
-    private List<MCPCompletionCandidate> completePlanIds(final 
MCPWorkflowHandlerContext handlerContext, final String sessionId) {
-        return 
handlerContext.getWorkflowSessionContext().list(sessionId).stream().filter(this::isCompletionEligiblePlan)
+    private List<MCPCompletionCandidate> completePlanIds(final 
MCPWorkflowHandlerContext handlerContext, final MCPCompletionRequestContext 
requestContext) {
+        Collection<String> workflowKinds = 
MCPDescriptorCatalogIndex.findWorkflowKindsByCompletionTarget(requestContext.getDescriptor());
+        return 
handlerContext.getWorkflowSessionContext().list(requestContext.getSessionId()).stream()
+                .filter(this::isCompletionEligiblePlan)
+                .filter(each -> isAllowedWorkflowKind(each, workflowKinds))
                 .map(each -> new MCPCompletionCandidate(each.getPlanId(), 
String.format("%s %s", each.getWorkflowKind(), each.getStatus()), 
"workflow-session", each.getUpdateTime(),
                         "recent-plan-first-for-plan_id"))
                 .toList();
@@ -63,4 +68,8 @@ public final class WorkflowPlanIdCompletionProvider 
implements MCPCompletionProv
     private boolean isCompletionEligiblePlan(final WorkflowContextSnapshot 
snapshot) {
         return 
COMPLETION_ELIGIBLE_WORKFLOW_STATUSES.contains(Objects.toString(snapshot.getStatus(),
 ""));
     }
+    
+    private boolean isAllowedWorkflowKind(final WorkflowContextSnapshot 
snapshot, final Collection<String> workflowKinds) {
+        return workflowKinds.isEmpty() || 
workflowKinds.contains(Objects.toString(snapshot.getWorkflowKind(), ""));
+    }
 }
diff --git 
a/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastFeatureDefinitionTest.java
 
b/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastFeatureDefinitionTest.java
index a03133c4686..cc654c09cef 100644
--- 
a/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastFeatureDefinitionTest.java
+++ 
b/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastFeatureDefinitionTest.java
@@ -25,7 +25,6 @@ import java.util.List;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class BroadcastFeatureDefinitionTest {
     
@@ -43,6 +42,6 @@ class BroadcastFeatureDefinitionTest {
     void assertPromptCompletionArguments() {
         MCPCompletionTargetDescriptor actual = 
MCPDescriptorCatalogLoader.load().getShardingSphereDescriptors().getCompletionTargetDescriptors().stream()
                 .filter(each -> "prompt".equals(each.getReferenceType()) && 
BroadcastFeatureDefinition.PLAN_PROMPT_NAME.equals(each.getReference())).findFirst().orElseThrow();
-        assertTrue(actual.getArguments().containsAll(List.of("database", 
"table", "plan_id")));
+        assertThat(actual.getArguments(), is(List.of("database", "table", 
"plan_id")));
     }
 }
diff --git 
a/mcp/features/encrypt/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-encrypt.yaml
 
b/mcp/features/encrypt/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-encrypt.yaml
index 0f6b6da6543..76462bfd7f4 100644
--- 
a/mcp/features/encrypt/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-encrypt.yaml
+++ 
b/mcp/features/encrypt/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-encrypt.yaml
@@ -478,7 +478,9 @@ tools:
               type: tool_call
               title: Call database_gateway_apply_workflow
               tool_name: database_gateway_apply_workflow
-              arguments: {}
+              arguments:
+                plan_id: encrypt-rule-20260505-001
+                execution_mode: preview
               reason: Preview generated encrypt rule DistSQL artifacts.
     annotations:
       title: Plan Encrypt Rule
diff --git 
a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptDescriptorContractTest.java
 
b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptDescriptorContractTest.java
index c4884a680cd..f9c7c72fc3c 100644
--- 
a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptDescriptorContractTest.java
+++ 
b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptDescriptorContractTest.java
@@ -28,6 +28,8 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -47,7 +49,8 @@ class EncryptDescriptorContractTest {
     
     @Test
     void assertPromptCompletionArguments() {
-        
assertCompletionTargetArguments(EncryptFeatureDefinition.PLAN_PROMPT_NAME, 
"database", "schema", "table", "column", "plan_id");
+        
assertCompletionTargetArguments(EncryptFeatureDefinition.PLAN_PROMPT_NAME, 
"database", "schema", "table", "column", "algorithm_type", 
"assisted_query_algorithm_type",
+                "like_query_algorithm_type", "plan_id");
     }
     
     private MCPToolDescriptor findToolDescriptor() {
@@ -59,7 +62,7 @@ class EncryptDescriptorContractTest {
     private void assertCompletionTargetArguments(final String promptName, 
final String... expectedArguments) {
         MCPCompletionTargetDescriptor actual = 
MCPDescriptorCatalogLoader.load().getShardingSphereDescriptors().getCompletionTargetDescriptors().stream()
                 .filter(each -> "prompt".equals(each.getReferenceType()) && 
promptName.equals(each.getReference())).findFirst().orElseThrow();
-        
assertTrue(actual.getArguments().containsAll(List.of(expectedArguments)));
+        assertThat(actual.getArguments(), is(List.of(expectedArguments)));
     }
     
     private void assertEncryptDistSQLExampleValue(final Object value) {
diff --git 
a/mcp/features/mask/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-mask.yaml
 
b/mcp/features/mask/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-mask.yaml
index 14572159980..dcf2ac10904 100644
--- 
a/mcp/features/mask/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-mask.yaml
+++ 
b/mcp/features/mask/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-mask.yaml
@@ -394,7 +394,9 @@ tools:
               type: tool_call
               title: Call database_gateway_apply_workflow
               tool_name: database_gateway_apply_workflow
-              arguments: {}
+              arguments:
+                plan_id: mask-rule-20260505-001
+                execution_mode: preview
               reason: Preview generated mask rule DistSQL artifacts.
     annotations:
       title: Plan Mask Rule
diff --git 
a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskFeatureDefinitionTest.java
 
b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskFeatureDefinitionTest.java
index bf5a1070fbf..1698de9b40e 100644
--- 
a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskFeatureDefinitionTest.java
+++ 
b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskFeatureDefinitionTest.java
@@ -25,7 +25,6 @@ import java.util.List;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class MaskFeatureDefinitionTest {
     
@@ -43,6 +42,6 @@ class MaskFeatureDefinitionTest {
     void assertPromptCompletionArguments() {
         MCPCompletionTargetDescriptor actual = 
MCPDescriptorCatalogLoader.load().getShardingSphereDescriptors().getCompletionTargetDescriptors().stream()
                 .filter(each -> "prompt".equals(each.getReferenceType()) && 
MaskFeatureDefinition.PLAN_PROMPT_NAME.equals(each.getReference())).findFirst().orElseThrow();
-        assertTrue(actual.getArguments().containsAll(List.of("database", 
"schema", "table", "column", "plan_id")));
+        assertThat(actual.getArguments(), is(List.of("database", "schema", 
"table", "column", "algorithm_type", "plan_id")));
     }
 }
diff --git 
a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingFeatureDefinitionTest.java
 
b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingFeatureDefinitionTest.java
index af9af0f3883..29e8738ed1e 100644
--- 
a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingFeatureDefinitionTest.java
+++ 
b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingFeatureDefinitionTest.java
@@ -26,7 +26,6 @@ import java.util.List;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class ReadwriteSplittingFeatureDefinitionTest {
     
@@ -39,13 +38,13 @@ class ReadwriteSplittingFeatureDefinitionTest {
     @Test
     void assertPromptCompletionArguments() {
         MCPDescriptorCatalog catalog = MCPDescriptorCatalogLoader.load();
-        assertCompletionTargetArguments(catalog, 
ReadwriteSplittingFeatureDefinition.PLAN_RULE_PROMPT_NAME, "database", 
"write_storage_unit", "plan_id");
+        assertCompletionTargetArguments(catalog, 
ReadwriteSplittingFeatureDefinition.PLAN_RULE_PROMPT_NAME, "database", 
"write_storage_unit", "load_balancer_type", "plan_id");
         assertCompletionTargetArguments(catalog, 
ReadwriteSplittingFeatureDefinition.PLAN_STATUS_PROMPT_NAME, "database", 
"storage_unit", "plan_id");
     }
     
     private void assertCompletionTargetArguments(final MCPDescriptorCatalog 
catalog, final String promptName, final String... expectedArguments) {
         MCPCompletionTargetDescriptor actual = 
catalog.getShardingSphereDescriptors().getCompletionTargetDescriptors().stream()
                 .filter(each -> "prompt".equals(each.getReferenceType()) && 
promptName.equals(each.getReference())).findFirst().orElseThrow();
-        
assertTrue(actual.getArguments().containsAll(List.of(expectedArguments)));
+        assertThat(actual.getArguments(), is(List.of(expectedArguments)));
     }
 }
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 ec673f023c0..5afbda9a43a 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
@@ -597,7 +597,15 @@ tools:
           distsql_artifacts: []
           resources_to_read: []
           proxy_topology_hint: {}
-          next_actions: []
+          next_actions:
+            - order: 1
+              type: tool_call
+              title: Call database_gateway_apply_workflow
+              tool_name: database_gateway_apply_workflow
+              arguments:
+                plan_id: shadow-rule-20260606-001
+                execution_mode: preview
+              reason: Preview generated shadow DistSQL artifacts.
     annotations:
       title: Plan Shadow Rule
       destructiveHint: false
@@ -678,7 +686,15 @@ tools:
           distsql_artifacts: []
           resources_to_read: []
           proxy_topology_hint: {}
-          next_actions: []
+          next_actions:
+            - order: 1
+              type: tool_call
+              title: Call database_gateway_apply_workflow
+              tool_name: database_gateway_apply_workflow
+              arguments:
+                plan_id: default-shadow-algorithm-20260606-001
+                execution_mode: preview
+              reason: Preview generated default shadow algorithm DistSQL 
artifacts.
     annotations:
       title: Plan Default Shadow Algorithm
       destructiveHint: false
@@ -749,7 +765,15 @@ tools:
           distsql_artifacts: []
           resources_to_read: []
           proxy_topology_hint: {}
-          next_actions: []
+          next_actions:
+            - order: 1
+              type: tool_call
+              title: Call database_gateway_apply_workflow
+              tool_name: database_gateway_apply_workflow
+              arguments:
+                plan_id: shadow-algorithm-cleanup-20260606-001
+                execution_mode: preview
+              reason: Preview generated shadow algorithm cleanup DistSQL 
artifacts.
     annotations:
       title: Plan Shadow Algorithm Cleanup
       destructiveHint: 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
index 60aa42a5bd6..dab093c5e5d 100644
--- 
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
@@ -29,7 +29,6 @@ import java.util.Map.Entry;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class ShadowDescriptorContractTest {
     
@@ -47,7 +46,7 @@ class ShadowDescriptorContractTest {
     @Test
     void assertPromptCompletionArguments() {
         MCPDescriptorCatalog catalog = MCPDescriptorCatalogLoader.load();
-        assertCompletionTargetArguments(catalog, 
ShadowFeatureDefinition.PLAN_RULE_PROMPT_NAME, "database", 
"source_storage_unit", "shadow_storage_unit", "plan_id");
+        assertCompletionTargetArguments(catalog, 
ShadowFeatureDefinition.PLAN_RULE_PROMPT_NAME, "database", 
"source_storage_unit", "shadow_storage_unit", "table", "algorithm_type", 
"plan_id");
         assertCompletionTargetArguments(catalog, 
ShadowFeatureDefinition.PLAN_DEFAULT_ALGORITHM_PROMPT_NAME, "database", 
"algorithm_type", "plan_id");
         assertCompletionTargetArguments(catalog, 
ShadowFeatureDefinition.PLAN_ALGORITHM_CLEANUP_PROMPT_NAME, "database", 
"plan_id");
     }
@@ -59,6 +58,6 @@ class ShadowDescriptorContractTest {
     private void assertCompletionTargetArguments(final MCPDescriptorCatalog 
catalog, final String promptName, final String... expectedArguments) {
         MCPCompletionTargetDescriptor actual = 
catalog.getShardingSphereDescriptors().getCompletionTargetDescriptors().stream()
                 .filter(each -> "prompt".equals(each.getReferenceType()) && 
promptName.equals(each.getReference())).findFirst().orElseThrow();
-        
assertTrue(actual.getArguments().containsAll(List.of(expectedArguments)));
+        assertThat(actual.getArguments(), is(List.of(expectedArguments)));
     }
 }
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 c0cc64fb2d2..b6ba5ecd493 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
@@ -855,7 +855,15 @@ tools:
           status: planned
           missing_required_inputs: []
           resources_to_read: []
-          next_actions: []
+          next_actions:
+            - order: 1
+              type: tool_call
+              title: Call database_gateway_apply_workflow
+              tool_name: database_gateway_apply_workflow
+              arguments:
+                plan_id: sharding-table-rule-20260606-001
+                execution_mode: preview
+              reason: Preview generated sharding DistSQL artifacts.
     annotations:
       title: Plan Sharding Table Rule
       destructiveHint: false
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
index 82a11750c2c..ae882e5d3d2 100644
--- 
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
@@ -29,7 +29,6 @@ import java.util.Map.Entry;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class ShardingDescriptorContractTest {
     
@@ -65,6 +64,6 @@ class ShardingDescriptorContractTest {
     private void assertCompletionTargetArguments(final MCPDescriptorCatalog 
catalog, final String promptName, final String... expectedArguments) {
         MCPCompletionTargetDescriptor actual = 
catalog.getShardingSphereDescriptors().getCompletionTargetDescriptors().stream()
                 .filter(each -> "prompt".equals(each.getReferenceType()) && 
promptName.equals(each.getReference())).findFirst().orElseThrow();
-        
assertTrue(actual.getArguments().containsAll(List.of(expectedArguments)));
+        assertThat(actual.getArguments(), is(List.of(expectedArguments)));
     }
 }
diff --git 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogIndex.java
 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogIndex.java
index 5778adf371a..e76e5793858 100644
--- 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogIndex.java
+++ 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogIndex.java
@@ -28,6 +28,7 @@ import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -57,6 +58,8 @@ public final class MCPDescriptorCatalogIndex {
     
     private static final Map<String, String> 
PLANNING_TOOL_NAMES_BY_WORKFLOW_KIND = createPlanningToolNamesByWorkflowKind();
     
+    private static final Map<String, Collection<String>> 
WORKFLOW_KINDS_BY_PROMPT_NAME = createWorkflowKindsByPromptName();
+    
     private static Map<String, MCPResourceDescriptor> 
createResourceDescriptors() {
         int expectedSize = 
CATALOG.getProtocolDescriptors().getResourceDescriptors().size() + 
CATALOG.getProtocolDescriptors().getResourceTemplateDescriptors().size();
         Map<String, MCPResourceDescriptor> result = new 
LinkedHashMap<>(expectedSize, 1F);
@@ -109,6 +112,36 @@ public final class MCPDescriptorCatalogIndex {
         return result;
     }
     
+    private static Map<String, Collection<String>> 
createWorkflowKindsByPromptName() {
+        Map<String, Collection<String>> result = new 
LinkedHashMap<>(PROMPT_DESCRIPTORS.size(), 1F);
+        for (MCPPromptDescriptor each : PROMPT_DESCRIPTORS) {
+            Collection<String> workflowKinds = 
findPromptPlanningWorkflowKind(each);
+            if (!workflowKinds.isEmpty()) {
+                result.put(each.getName(), workflowKinds);
+            }
+        }
+        return result;
+    }
+    
+    private static Collection<String> findPromptPlanningWorkflowKind(final 
MCPPromptDescriptor prompt) {
+        String planningToolName = "database_gateway_" + prompt.getName();
+        Object relatedTools = 
prompt.getMeta().get(MCPShardingSphereMetadataKeys.RELATED_TOOLS);
+        MCPToolDescriptor toolDescriptor = 
TOOL_DESCRIPTORS.get(planningToolName);
+        if (!(relatedTools instanceof Collection<?>)) {
+            return List.of();
+        }
+        if (!((Collection<?>) relatedTools).contains(planningToolName) || null 
== toolDescriptor || !isPlanningTool(planningToolName)) {
+            return List.of();
+        }
+        String workflowKind = 
Objects.toString(toolDescriptor.getMeta().get(MCPShardingSphereMetadataKeys.WORKFLOW_KIND),
 "");
+        return workflowKind.isEmpty() ? List.of() : List.of(workflowKind);
+    }
+    
+    private static boolean isPlanningTool(final String toolName) {
+        MCPToolRuntimeDescriptor runtimeDescriptor = 
TOOL_RUNTIME_DESCRIPTORS.get(toolName);
+        return null != runtimeDescriptor && 
"plan".equals(runtimeDescriptor.getWorkflowRole());
+    }
+    
     /**
      * Get resource descriptors.
      *
@@ -198,6 +231,19 @@ public final class MCPDescriptorCatalogIndex {
         return 
Optional.ofNullable(PLANNING_TOOL_NAMES_BY_WORKFLOW_KIND.get(workflowKind));
     }
     
+    /**
+     * Find workflow kinds related to a completion target.
+     *
+     * @param descriptor completion target descriptor
+     * @return related workflow kinds
+     */
+    public static Collection<String> findWorkflowKindsByCompletionTarget(final 
MCPCompletionTargetDescriptor descriptor) {
+        if (!"prompt".equals(descriptor.getReferenceType())) {
+            return List.of();
+        }
+        return 
WORKFLOW_KINDS_BY_PROMPT_NAME.getOrDefault(descriptor.getReference(), 
List.of()).stream().toList();
+    }
+    
     /**
      * Get completion target descriptors.
      *
diff --git 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogIndexTest.java
 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogIndexTest.java
index b45268d4eeb..c49cd26481a 100644
--- 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogIndexTest.java
+++ 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogIndexTest.java
@@ -107,6 +107,18 @@ class MCPDescriptorCatalogIndexTest {
         
assertFalse(MCPDescriptorCatalogIndex.findPlanningToolNameByWorkflowKind("unknown.rule").isPresent());
     }
     
+    @Test
+    void assertFindWorkflowKindsByGenericPromptCompletionTarget() {
+        MCPCompletionTargetDescriptor descriptor = new 
MCPCompletionTargetDescriptor("prompt", "recover_workflow", List.of("plan_id"), 
50, Map.of());
+        
assertTrue(MCPDescriptorCatalogIndex.findWorkflowKindsByCompletionTarget(descriptor).isEmpty());
+    }
+    
+    @Test
+    void assertFindWorkflowKindsByResourceCompletionTarget() {
+        MCPCompletionTargetDescriptor descriptor = new 
MCPCompletionTargetDescriptor("resource", 
"shardingsphere://workflows/{plan_id}", List.of("plan_id"), 50, Map.of());
+        
assertTrue(MCPDescriptorCatalogIndex.findWorkflowKindsByCompletionTarget(descriptor).isEmpty());
+    }
+    
     @Test
     void assertGetCompletionTargetDescriptors() {
         Collection<MCPCompletionTargetDescriptor> actualDescriptors = 
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors();
diff --git 
a/mcp/support/src/test/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-test-planning.yaml
 
b/mcp/support/src/test/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-test-planning.yaml
index fb162f71163..f320c7dabb9 100644
--- 
a/mcp/support/src/test/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-test-planning.yaml
+++ 
b/mcp/support/src/test/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-test-planning.yaml
@@ -200,7 +200,9 @@ tools:
               type: tool_call
               title: Call database_gateway_apply_workflow
               tool_name: database_gateway_apply_workflow
-              arguments: {}
+              arguments:
+                plan_id: encrypt-rule-20260505-001
+                execution_mode: preview
               reason: Preview generated workflow artifacts.
     annotations:
       title: Plan Encrypt Rule

Reply via email to