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 32a929182f8 Standardize feature descriptors and fix completions
(#38833)
32a929182f8 is described below
commit 32a929182f8d160d1779b2f7db9442ae7932a954
Author: Liang Zhang <[email protected]>
AuthorDate: Tue Jun 9 10:16:48 2026 +0800
Standardize feature descriptors and fix completions (#38833)
* Complete encrypt feature template contract
- Add missing encrypt workflow output schema fields
- Align encrypt prompt with assisted and LIKE query algorithm inputs
- Enforce template output fields in descriptor validation
- Cover completion targets, SPI registration, and secret-safe candidates
- Mark Speckit traceability tasks as complete
* Standardize feature descriptors and fix completions
- align feature descriptor content, style, and completion design across MCP
feature modules
- fix feature plugin completions by using prompt targets instead of fixed
resources
- add fallback plugin catalogs and regression coverage for
readwrite-splitting, shadow, and sharding
- update MCP E2E baseline contract and validation to prevent static
resource completion regressions
* Standardize feature descriptors and fix completions
- align feature descriptor content, style, and completion design across MCP
feature modules
- fix feature plugin completions by using prompt targets instead of fixed
resources
- add fallback plugin catalogs and regression coverage for
readwrite-splitting, shadow, and sharding
- update MCP E2E baseline contract and validation to prevent static
resource completion regressions
---
.../descriptor/EncryptToolDescriptorValidator.java | 10 ++-
.../mcp-descriptors/mcp-descriptor-encrypt.yaml | 44 +++++++++++--
.../prompts/plan-encrypt-rule.md | 2 +
.../encrypt/EncryptMCPHandlerProviderTest.java | 8 +++
.../EncryptAlgorithmCompletionProviderTest.java | 60 ++++++++++++-----
.../EncryptToolDescriptorValidatorTest.java | 51 ++++++++++++++-
.../mcp-descriptors/mcp-descriptor-mask.yaml | 5 --
.../MaskToolDescriptorValidatorTest.java | 8 ++-
.../ReadwriteSplittingInspectionService.java | 15 ++++-
...re.mcp.support.completion.MCPCompletionProvider | 17 +++++
.../mcp-descriptor-readwrite-splitting.yaml | 5 --
...dwriteSplittingToolDescriptorValidatorTest.java | 9 ++-
.../ReadwriteSplittingInspectionServiceTest.java | 25 ++++++-
.../tool/service/ShadowInspectionService.java | 15 ++++-
...re.mcp.support.completion.MCPCompletionProvider | 17 +++++
.../mcp-descriptors/mcp-descriptor-shadow.yaml | 5 --
.../ShadowToolDescriptorValidatorTest.java | 8 ++-
.../tool/service/ShadowInspectionServiceTest.java | 25 ++++++-
.../tool/service/ShardingInspectionService.java | 35 +++++++++-
...re.mcp.support.completion.MCPCompletionProvider | 17 +++++
.../mcp-descriptors/mcp-descriptor-sharding.yaml | 76 +++++++++++++++++++---
.../ShardingToolDescriptorValidatorTest.java | 15 ++++-
.../service/ShardingInspectionServiceTest.java | 45 ++++++++++++-
.../descriptor/MCPDescriptorCatalogValidator.java | 19 +++++-
.../descriptor/MCPDescriptorCatalogIndexTest.java | 2 +
.../HttpTransportCompletionE2ETest.java | 27 +++++---
.../model-contract/capabilities.yaml | 42 +++++++++---
27 files changed, 517 insertions(+), 90 deletions(-)
diff --git
a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/descriptor/EncryptToolDescriptorValidator.java
b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/descriptor/EncryptToolDescriptorValidator.java
index 3d9e7b9cfdb..3d510110cec 100644
---
a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/descriptor/EncryptToolDescriptorValidator.java
+++
b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/descriptor/EncryptToolDescriptorValidator.java
@@ -23,6 +23,7 @@ import
org.apache.shardingsphere.mcp.support.descriptor.MCPToolDescriptorValidat
import
org.apache.shardingsphere.mcp.support.descriptor.MCPToolDescriptorValidationUtils;
import org.apache.shardingsphere.mcp.support.protocol.MCPPayloadFieldNames;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
+import
org.apache.shardingsphere.mcp.support.workflow.service.WorkflowArtifactPayloadUtils;
import java.util.List;
@@ -31,6 +32,11 @@ import java.util.List;
*/
public final class EncryptToolDescriptorValidator implements
MCPToolDescriptorValidator {
+ private static final List<String> REQUIRED_OUTPUT_FIELDS =
List.of("response_mode", WorkflowFieldNames.PLAN_ID, "workflow_kind", "status",
"missing_required_inputs",
+ MCPPayloadFieldNames.RESOURCES_TO_READ,
MCPPayloadFieldNames.NEXT_ACTIONS, "argument_provenance", "proxy_topology_hint",
+ WorkflowArtifactPayloadUtils.PAYLOAD_KEY_DISTSQL_ARTIFACTS,
WorkflowArtifactPayloadUtils.PAYLOAD_KEY_DDL_ARTIFACTS,
WorkflowArtifactPayloadUtils.PAYLOAD_KEY_INDEX_PLAN,
+ "derived_column_plan", "masked_property_preview");
+
@Override
public boolean supports(final MCPToolDescriptor toolDescriptor) {
return
EncryptFeatureDefinition.PLAN_TOOL_NAME.equals(toolDescriptor.getName());
@@ -38,8 +44,6 @@ public final class EncryptToolDescriptorValidator implements
MCPToolDescriptorVa
@Override
public void validate(final MCPToolDescriptor toolDescriptor) {
-
MCPToolDescriptorValidationUtils.validateRequiredOutputFields(toolDescriptor,
- List.of("response_mode", WorkflowFieldNames.PLAN_ID,
"workflow_kind", "status", "missing_required_inputs",
- MCPPayloadFieldNames.RESOURCES_TO_READ,
MCPPayloadFieldNames.NEXT_ACTIONS));
+
MCPToolDescriptorValidationUtils.validateRequiredOutputFields(toolDescriptor,
REQUIRED_OUTPUT_FIELDS);
}
}
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 e97032d8f7e..cfd766ae558 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
@@ -132,13 +132,6 @@ completionTargets:
- assisted_query_algorithm_type
- like_query_algorithm_type
maxValues: 50
- - referenceType: resource
- reference: shardingsphere://features/encrypt/algorithms
- arguments:
- - algorithm_type
- - assisted_query_algorithm_type
- - like_query_algorithm_type
- maxValues: 50
resourceNavigation:
- from: shardingsphere://features/encrypt/algorithms
to: database_gateway_plan_encrypt_rule
@@ -377,9 +370,46 @@ tools:
intent_inference:
type: object
description: "Resolved operation type, inferred values, unresolved
fields, and reasoning notes."
+ argument_provenance:
+ type: object
+ description: "Source of important workflow arguments, such as
user-provided, inferred, or server-defaulted values."
review_focus:
type: object
description: "Compact planning review focus: artifact categories,
side-effect scope, manual-only mode, and next review action."
+ proxy_topology_hint:
+ type: object
+ description: "Proxy metadata view expected by this workflow and safe
recovery guidance when runtime metadata looks inconsistent."
+ distsql_artifacts:
+ type: array
+ description: "Masked rule DistSQL artifacts generated for review
before applying the workflow."
+ items:
+ type: object
+ ddl_artifacts:
+ type: array
+ description: "Reviewable DDL artifact list returned by workflow
payloads; encrypt rule planning normally leaves this empty."
+ items:
+ type: object
+ index_plan:
+ type: array
+ description: "Reviewable index plan list returned by workflow
payloads; encrypt rule planning normally leaves this empty."
+ items:
+ type: object
+ derived_column_plan:
+ type: object
+ description: "Encrypt-specific physical column plan for cipher,
assisted-query, and LIKE-query columns."
+ masked_property_preview:
+ type: object
+ description: "Encrypt-specific preview of algorithm properties with
secret values masked before returning them to MCP clients."
+ properties:
+ primary:
+ type: object
+ description: "Masked primary encrypt algorithm properties."
+ assisted_query:
+ type: object
+ description: "Masked assisted-query algorithm properties."
+ like_query:
+ type: object
+ description: "Masked LIKE-query algorithm properties."
next_actions:
type: array
description: "Ordered model-facing recommendations for the next call
or user question."
diff --git
a/mcp/features/encrypt/src/main/resources/META-INF/shardingsphere-mcp/prompts/plan-encrypt-rule.md
b/mcp/features/encrypt/src/main/resources/META-INF/shardingsphere-mcp/prompts/plan-encrypt-rule.md
index c6492306bc6..5d45e14066b 100644
---
a/mcp/features/encrypt/src/main/resources/META-INF/shardingsphere-mcp/prompts/plan-encrypt-rule.md
+++
b/mcp/features/encrypt/src/main/resources/META-INF/shardingsphere-mcp/prompts/plan-encrypt-rule.md
@@ -23,6 +23,8 @@ User context:
- table: {{table}}
- column: {{column}}
- algorithm_type: {{algorithm_type}}
+- assisted_query_algorithm_type: {{assisted_query_algorithm_type}}
+- like_query_algorithm_type: {{like_query_algorithm_type}}
- plan_id: {{plan_id}}
Model path:
diff --git
a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProviderTest.java
b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProviderTest.java
index 83f65b549ee..12ef9c24558 100644
---
a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProviderTest.java
+++
b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProviderTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.feature.encrypt;
+import org.apache.shardingsphere.mcp.api.MCPHandlerProvider;
import org.apache.shardingsphere.mcp.api.resource.MCPResourceHandler;
import org.apache.shardingsphere.mcp.api.tool.MCPToolHandler;
import
org.apache.shardingsphere.mcp.feature.encrypt.tool.service.EncryptWorkflowValidationService;
@@ -25,10 +26,12 @@ import org.junit.jupiter.api.Test;
import java.util.Collection;
import java.util.List;
+import java.util.ServiceLoader;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isA;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class EncryptMCPHandlerProviderTest {
@@ -47,6 +50,11 @@ class EncryptMCPHandlerProviderTest {
assertThat(actual.getToolName(),
is("database_gateway_plan_encrypt_rule"));
}
+ @Test
+ void assertLoadByServiceLoader() {
+
assertTrue(ServiceLoader.load(MCPHandlerProvider.class).stream().map(ServiceLoader.Provider::type).anyMatch(EncryptMCPHandlerProvider.class::equals));
+ }
+
@Test
void assertGetWorkflowDefinitions() {
WorkflowRuntimeDefinition actual = new
EncryptMCPHandlerProvider().getWorkflowDefinitions().iterator().next();
diff --git
a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/completion/EncryptAlgorithmCompletionProviderTest.java
b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/completion/EncryptAlgorithmCompletionProviderTest.java
index d192e9a8202..c056de096a9 100644
---
a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/completion/EncryptAlgorithmCompletionProviderTest.java
+++
b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/completion/EncryptAlgorithmCompletionProviderTest.java
@@ -19,20 +19,26 @@ package
org.apache.shardingsphere.mcp.feature.encrypt.completion;
import org.apache.shardingsphere.mcp.feature.encrypt.EncryptFeatureDefinition;
import org.apache.shardingsphere.mcp.support.completion.MCPCompletionCandidate;
-import
org.apache.shardingsphere.mcp.support.completion.MCPCompletionProviderResult;
+import org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider;
import
org.apache.shardingsphere.mcp.support.completion.MCPCompletionRequestContext;
import
org.apache.shardingsphere.mcp.support.database.MCPDatabaseHandlerContext;
import
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
import
org.apache.shardingsphere.mcp.support.descriptor.MCPCompletionTargetDescriptor;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.ServiceLoader;
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.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -45,37 +51,57 @@ class EncryptAlgorithmCompletionProviderTest {
}
@Test
- void assertSupports() {
- assertTrue(new
EncryptAlgorithmCompletionProvider().supports(createRequestContext(EncryptFeatureDefinition.PLAN_PROMPT_NAME,
"algorithm_type")));
+ void assertLoadByServiceLoader() {
+
assertTrue(ServiceLoader.load(MCPCompletionProvider.class).stream().map(ServiceLoader.Provider::type).anyMatch(EncryptAlgorithmCompletionProvider.class::equals));
}
- @Test
- void assertSupportsWithAlgorithmResource() {
- assertTrue(new
EncryptAlgorithmCompletionProvider().supports(createRequestContext(EncryptFeatureDefinition.ALGORITHMS_RESOURCE_URI,
"like_query_algorithm_type")));
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("supportedReferences")
+ void assertSupports(final String name, final String referenceType, final
String reference, final String argumentName) {
+ assertTrue(new
EncryptAlgorithmCompletionProvider().supports(createRequestContext(referenceType,
reference, argumentName)));
}
- @Test
- void assertSupportsWithForeignReference() {
- assertFalse(new
EncryptAlgorithmCompletionProvider().supports(createRequestContext("plan_mask_rule",
"algorithm_type")));
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("unsupportedReferences")
+ void assertSupportsWithForeignReference(final String name, final String
referenceType, final String reference, final String argumentName) {
+ assertFalse(new
EncryptAlgorithmCompletionProvider().supports(createRequestContext(referenceType,
reference, argumentName)));
}
@Test
void assertComplete() {
MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
when(queryFacade.queryWithAnyDatabase("SHOW ENCRYPT ALGORITHM
PLUGINS")).thenReturn(List.of(
- Map.of("type", "AES", "description", "AES encryptor"),
+ Map.of("type", "AES", "description", "AES encryptor",
"aes-key-value", "secret-value"),
Map.of("type", "")));
MCPDatabaseHandlerContext handlerContext =
mock(MCPDatabaseHandlerContext.class);
when(handlerContext.getQueryFacade()).thenReturn(queryFacade);
- MCPCompletionProviderResult actual = new
EncryptAlgorithmCompletionProvider().complete(handlerContext,
-
createRequestContext(EncryptFeatureDefinition.PLAN_PROMPT_NAME,
"algorithm_type"));
- List<MCPCompletionCandidate> actualCandidates = new
ArrayList<>(actual.getCandidates());
+ List<MCPCompletionCandidate> actualCandidates = new ArrayList<>(new
EncryptAlgorithmCompletionProvider().complete(handlerContext,
+ createRequestContext("prompt",
EncryptFeatureDefinition.PLAN_PROMPT_NAME, "algorithm_type")).getCandidates());
assertThat(actualCandidates.size(), is(1));
- assertThat(actualCandidates.get(0).getValue(), is("AES"));
- assertThat(actualCandidates.get(0).getLabel(), is("AES encryptor"));
+ assertThat(actualCandidates.getFirst().getValue(), is("AES"));
+ assertThat(actualCandidates.getFirst().getLabel(), is("AES
encryptor"));
+ assertThat(actualCandidates.getFirst().getSource(),
is("encrypt-algorithm"));
+ assertNull(actualCandidates.getFirst().getUpdateTime());
+ assertThat(actualCandidates.getFirst().getRankingReason(), is(""));
+ }
+
+ private static Collection<Arguments> supportedReferences() {
+ return List.of(
+ Arguments.of("prompt algorithm_type", "prompt",
EncryptFeatureDefinition.PLAN_PROMPT_NAME, "algorithm_type"),
+ Arguments.of("prompt assisted_query_algorithm_type", "prompt",
EncryptFeatureDefinition.PLAN_PROMPT_NAME, "assisted_query_algorithm_type"),
+ Arguments.of("prompt like_query_algorithm_type", "prompt",
EncryptFeatureDefinition.PLAN_PROMPT_NAME, "like_query_algorithm_type"),
+ Arguments.of("resource algorithm_type", "resource",
EncryptFeatureDefinition.ALGORITHMS_RESOURCE_URI, "algorithm_type"),
+ Arguments.of("resource assisted_query_algorithm_type",
"resource", EncryptFeatureDefinition.ALGORITHMS_RESOURCE_URI,
"assisted_query_algorithm_type"),
+ Arguments.of("resource like_query_algorithm_type", "resource",
EncryptFeatureDefinition.ALGORITHMS_RESOURCE_URI, "like_query_algorithm_type"));
+ }
+
+ private static Collection<Arguments> unsupportedReferences() {
+ return List.of(
+ Arguments.of("foreign prompt", "prompt", "plan_mask_rule",
"algorithm_type"),
+ Arguments.of("foreign resource", "resource",
"shardingsphere://features/mask/algorithms", "algorithm_type"));
}
- private MCPCompletionRequestContext createRequestContext(final String
reference, final String argumentName) {
- return new MCPCompletionRequestContext("session-1", new
MCPCompletionTargetDescriptor("prompt", reference, List.of(argumentName), 50,
Map.of()), argumentName, Map.of());
+ private MCPCompletionRequestContext createRequestContext(final String
referenceType, final String reference, final String argumentName) {
+ return new MCPCompletionRequestContext("session-1", new
MCPCompletionTargetDescriptor(referenceType, reference, List.of(argumentName),
50, Map.of()), argumentName, Map.of());
}
}
diff --git
a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/descriptor/EncryptToolDescriptorValidatorTest.java
b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/descriptor/EncryptToolDescriptorValidatorTest.java
index 8164012690e..c2137f2e557 100644
---
a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/descriptor/EncryptToolDescriptorValidatorTest.java
+++
b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/descriptor/EncryptToolDescriptorValidatorTest.java
@@ -23,7 +23,10 @@ import
org.apache.shardingsphere.mcp.feature.encrypt.EncryptFeatureDefinition;
import
org.apache.shardingsphere.mcp.support.descriptor.MCPCompletionTargetDescriptor;
import
org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalogIndex;
import
org.apache.shardingsphere.mcp.support.descriptor.MCPShardingSphereMetadataKeys;
+import
org.apache.shardingsphere.mcp.support.descriptor.MCPToolDescriptorValidator;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import java.io.IOException;
import java.io.InputStream;
@@ -31,6 +34,7 @@ import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.ServiceLoader;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@@ -45,6 +49,11 @@ class EncryptToolDescriptorValidatorTest {
assertTrue(new
EncryptToolDescriptorValidator().supports(MCPDescriptorCatalogIndex.getRequiredToolDescriptor(EncryptFeatureDefinition.PLAN_TOOL_NAME)));
}
+ @Test
+ void assertLoadByServiceLoader() {
+
assertTrue(ServiceLoader.load(MCPToolDescriptorValidator.class).stream().map(ServiceLoader.Provider::type).anyMatch(EncryptToolDescriptorValidator.class::equals));
+ }
+
@Test
void assertPromptUsesGuidanceName() {
MCPPromptDescriptor actual =
findPrompt(EncryptFeatureDefinition.PLAN_PROMPT_NAME);
@@ -58,8 +67,15 @@ class EncryptToolDescriptorValidatorTest {
List<String> expectedArguments = List.of("algorithm_type",
"assisted_query_algorithm_type", "like_query_algorithm_type");
MCPCompletionTargetDescriptor promptCompletionTarget =
findCompletionTarget("prompt", EncryptFeatureDefinition.PLAN_PROMPT_NAME);
assertThat(promptCompletionTarget.getArguments(),
is(expectedArguments));
- MCPCompletionTargetDescriptor resourceCompletionTarget =
findCompletionTarget("resource",
EncryptFeatureDefinition.ALGORITHMS_RESOURCE_URI);
- assertThat(resourceCompletionTarget.getArguments(),
is(expectedArguments));
+ assertFalse(hasCompletionTarget("resource",
EncryptFeatureDefinition.ALGORITHMS_RESOURCE_URI));
+ }
+
+ @Test
+ void assertPromptExposesCompletionArguments() throws IOException {
+ String actual =
readResource("META-INF/shardingsphere-mcp/prompts/plan-encrypt-rule.md");
+ for (String each : findCompletionTarget("prompt",
EncryptFeatureDefinition.PLAN_PROMPT_NAME).getArguments()) {
+ assertTrue(actual.contains("- " + each + ": {{" + each + "}}"));
+ }
}
@Test
@@ -75,6 +91,18 @@ class EncryptToolDescriptorValidatorTest {
assertTrue(supportProperties.containsKey("selected_interaction"));
}
+ @Test
+ @SuppressWarnings("unchecked")
+ void assertOutputSchemaDeclaresPlanResponseFields() {
+ MCPToolDescriptor descriptor =
MCPDescriptorCatalogIndex.getRequiredToolDescriptor(EncryptFeatureDefinition.PLAN_TOOL_NAME);
+ Map<String, Object> actual = (Map<String, Object>)
descriptor.getOutputSchema().get("properties");
+ assertTrue(actual.keySet().containsAll(List.of(
+ "response_mode", "plan_id", "workflow_kind", "status",
"issues", "global_steps", "current_step", "algorithm_recommendations",
+ "property_requirements", "validation_strategy",
"delivery_mode", "execution_mode", "intent_inference", "argument_provenance",
"review_focus",
+ "missing_required_inputs", "clarification_questions",
"resources_to_read", "proxy_topology_hint", "next_actions", "distsql_artifacts",
+ "ddl_artifacts", "index_plan", "derived_column_plan",
"masked_property_preview")));
+ }
+
@Test
@SuppressWarnings("unchecked")
void assertDescriptorIsRuleDistSQLOnly() {
@@ -112,6 +140,20 @@ class EncryptToolDescriptorValidatorTest {
assertThat(actual.getMessage(), is("Tool
`database_gateway_plan_encrypt_rule` outputSchema must declare
`resources_to_read`."));
}
+ @ParameterizedTest(name = "{0}")
+ @ValueSource(strings = {"argument_provenance", "masked_property_preview"})
+ @SuppressWarnings("unchecked")
+ void assertValidateRejectsMissingTemplateOutputField(final String
fieldName) {
+ MCPToolDescriptor descriptor =
MCPDescriptorCatalogIndex.getRequiredToolDescriptor(EncryptFeatureDefinition.PLAN_TOOL_NAME);
+ Map<String, Object> outputSchema = new
LinkedHashMap<>(descriptor.getOutputSchema());
+ Map<String, Object> properties = new LinkedHashMap<>((Map<String,
Object>) outputSchema.get("properties"));
+ properties.remove(fieldName);
+ outputSchema.put("properties", properties);
+ IllegalStateException actual =
assertThrows(IllegalStateException.class, () -> new
EncryptToolDescriptorValidator().validate(new MCPToolDescriptor(
+ descriptor.getName(), descriptor.getTitle(),
descriptor.getDescription(), descriptor.getInputSchema(), outputSchema,
descriptor.getAnnotations(), descriptor.getMeta())));
+ assertThat(actual.getMessage(), is("Tool
`database_gateway_plan_encrypt_rule` outputSchema must declare `" + fieldName +
"`."));
+ }
+
private MCPPromptDescriptor findPrompt(final String promptName) {
return
MCPDescriptorCatalogIndex.getPromptDescriptors().stream().filter(each ->
promptName.equals(each.getName())).findFirst().orElseThrow();
}
@@ -121,6 +163,11 @@ class EncryptToolDescriptorValidatorTest {
.filter(each -> referenceType.equals(each.getReferenceType())
&& reference.equals(each.getReference())).findFirst().orElseThrow();
}
+ private boolean hasCompletionTarget(final String referenceType, final
String reference) {
+ return
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors().stream()
+ .anyMatch(each ->
referenceType.equals(each.getReferenceType()) &&
reference.equals(each.getReference()));
+ }
+
private String readResource(final String resourceName) throws IOException {
try (InputStream inputStream =
Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName))
{
return new String(inputStream.readAllBytes(),
StandardCharsets.UTF_8);
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 3b765fed567..7662e4b011e 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
@@ -122,11 +122,6 @@ completionTargets:
arguments:
- algorithm_type
maxValues: 50
- - referenceType: resource
- reference: shardingsphere://features/mask/algorithms
- arguments:
- - algorithm_type
- maxValues: 50
resourceNavigation:
- from: shardingsphere://features/mask/algorithms
to: database_gateway_plan_mask_rule
diff --git
a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/descriptor/MaskToolDescriptorValidatorTest.java
b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/descriptor/MaskToolDescriptorValidatorTest.java
index e736a612414..b04082fe2d6 100644
---
a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/descriptor/MaskToolDescriptorValidatorTest.java
+++
b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/descriptor/MaskToolDescriptorValidatorTest.java
@@ -57,8 +57,7 @@ class MaskToolDescriptorValidatorTest {
void assertExposeCompletionTargets() {
MCPCompletionTargetDescriptor promptCompletionTarget =
findCompletionTarget("prompt", MaskFeatureDefinition.PLAN_PROMPT_NAME);
assertThat(promptCompletionTarget.getArguments(),
is(List.of("algorithm_type")));
- MCPCompletionTargetDescriptor resourceCompletionTarget =
findCompletionTarget("resource", MaskFeatureDefinition.ALGORITHMS_RESOURCE_URI);
- assertThat(resourceCompletionTarget.getArguments(),
is(List.of("algorithm_type")));
+ assertFalse(hasCompletionTarget("resource",
MaskFeatureDefinition.ALGORITHMS_RESOURCE_URI));
}
@Test
@@ -126,6 +125,11 @@ class MaskToolDescriptorValidatorTest {
.filter(each -> referenceType.equals(each.getReferenceType())
&& reference.equals(each.getReference())).findFirst().orElseThrow();
}
+ private boolean hasCompletionTarget(final String referenceType, final
String reference) {
+ return
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors().stream()
+ .anyMatch(each ->
referenceType.equals(each.getReferenceType()) &&
reference.equals(each.getReference()));
+ }
+
private String readResource(final String resourceName) throws IOException {
try (InputStream inputStream =
Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName))
{
return new String(inputStream.readAllBytes(),
StandardCharsets.UTF_8);
diff --git
a/mcp/features/readwrite-splitting/src/main/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/tool/service/ReadwriteSplittingInspectionService.java
b/mcp/features/readwrite-splitting/src/main/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/tool/service/ReadwriteSplittingInspectionService.java
index 25ddc200329..369f94e0f16 100644
---
a/mcp/features/readwrite-splitting/src/main/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/tool/service/ReadwriteSplittingInspectionService.java
+++
b/mcp/features/readwrite-splitting/src/main/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/tool/service/ReadwriteSplittingInspectionService.java
@@ -17,7 +17,9 @@
package org.apache.shardingsphere.mcp.feature.readwritesplitting.tool.service;
+import
org.apache.shardingsphere.mcp.api.protocol.exception.MCPQueryFailedException;
import
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
+import
org.apache.shardingsphere.mcp.support.workflow.service.WorkflowDistSQLQueryUtils;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowSQLUtils;
import java.util.LinkedHashMap;
@@ -96,7 +98,18 @@ public final class ReadwriteSplittingInspectionService {
* @return algorithm plugin rows with built-in property hints
*/
public List<Map<String, Object>> queryLoadBalanceAlgorithmPlugins(final
MCPFeatureQueryFacade queryFacade) {
- return queryFacade.queryWithAnyDatabase("SHOW LOAD BALANCE ALGORITHM
PLUGINS").stream().map(this::appendPropertyGuidance).toList();
+ return
queryAlgorithmRows(queryFacade).stream().map(this::appendPropertyGuidance).toList();
+ }
+
+ private List<Map<String, Object>> queryAlgorithmRows(final
MCPFeatureQueryFacade queryFacade) {
+ try {
+ return queryFacade.queryWithAnyDatabase("SHOW LOAD BALANCE
ALGORITHM PLUGINS");
+ } catch (final MCPQueryFailedException ex) {
+ if
(WorkflowDistSQLQueryUtils.isUnsupportedDistSQLQueryFailure(ex)) {
+ return List.of(Map.of("type", "RANDOM"), Map.of("type",
"ROUND_ROBIN"), Map.of("type", "WEIGHT"));
+ }
+ throw ex;
+ }
}
private Map<String, Object> appendPropertyGuidance(final Map<String,
Object> row) {
diff --git
a/mcp/features/readwrite-splitting/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
b/mcp/features/readwrite-splitting/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
index 596414d6512..e9c5a8d4902 100644
---
a/mcp/features/readwrite-splitting/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
+++
b/mcp/features/readwrite-splitting/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
@@ -1 +1,18 @@
+#
+# 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.
+#
+
org.apache.shardingsphere.mcp.feature.readwritesplitting.completion.ReadwriteSplittingLoadBalanceAlgorithmCompletionProvider
diff --git
a/mcp/features/readwrite-splitting/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-readwrite-splitting.yaml
b/mcp/features/readwrite-splitting/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-readwrite-splitting.yaml
index 1d539363ef5..55bcc9eec2f 100644
---
a/mcp/features/readwrite-splitting/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-readwrite-splitting.yaml
+++
b/mcp/features/readwrite-splitting/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-readwrite-splitting.yaml
@@ -212,11 +212,6 @@ completionTargets:
arguments:
- load_balancer_type
maxValues: 50
- - referenceType: resource
- reference:
shardingsphere://features/readwrite-splitting/load-balance-algorithm-plugins
- arguments:
- - load_balancer_type
- maxValues: 50
resourceNavigation:
- from:
shardingsphere://features/readwrite-splitting/databases/{database}/rules
to: database_gateway_plan_readwrite_splitting_rule
diff --git
a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/descriptor/ReadwriteSplittingToolDescriptorValidatorTest.java
b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/descriptor/ReadwriteSplittingToolDescriptorValidatorTest.java
index 2f0dfe110b8..b3abc64a117 100644
---
a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/descriptor/ReadwriteSplittingToolDescriptorValidatorTest.java
+++
b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/descriptor/ReadwriteSplittingToolDescriptorValidatorTest.java
@@ -52,9 +52,7 @@ class ReadwriteSplittingToolDescriptorValidatorTest {
MCPCompletionTargetDescriptor promptCompletionTarget =
findCompletionTarget("prompt",
ReadwriteSplittingFeatureDefinition.PLAN_RULE_PROMPT_NAME);
assertThat(promptCompletionTarget.getArguments(),
is(List.of(ReadwriteSplittingFeatureDefinition.LOAD_BALANCER_TYPE_FIELD)));
assertThat(promptCompletionTarget.getMaxValues(), is(50));
- MCPCompletionTargetDescriptor resourceCompletionTarget =
findCompletionTarget("resource",
ReadwriteSplittingFeatureDefinition.LOAD_BALANCE_ALGORITHM_PLUGINS_RESOURCE_URI);
- assertThat(resourceCompletionTarget.getArguments(),
is(List.of(ReadwriteSplittingFeatureDefinition.LOAD_BALANCER_TYPE_FIELD)));
- assertThat(resourceCompletionTarget.getMaxValues(), is(50));
+ assertFalse(hasCompletionTarget("resource",
ReadwriteSplittingFeatureDefinition.LOAD_BALANCE_ALGORITHM_PLUGINS_RESOURCE_URI));
}
@Test
@@ -114,6 +112,11 @@ class ReadwriteSplittingToolDescriptorValidatorTest {
.filter(each -> referenceType.equals(each.getReferenceType())
&& reference.equals(each.getReference())).findFirst().orElseThrow();
}
+ private boolean hasCompletionTarget(final String referenceType, final
String reference) {
+ return
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors().stream()
+ .anyMatch(each ->
referenceType.equals(each.getReferenceType()) &&
reference.equals(each.getReference()));
+ }
+
private String readResource(final String resourceName) throws IOException {
try (InputStream inputStream =
Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName))
{
return new String(inputStream.readAllBytes(),
StandardCharsets.UTF_8);
diff --git
a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/tool/service/ReadwriteSplittingInspectionServiceTest.java
b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/tool/service/ReadwriteSplittingInspectionServiceTest.java
index bdf86890fb0..6f26b996018 100644
---
a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/tool/service/ReadwriteSplittingInspectionServiceTest.java
+++
b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/tool/service/ReadwriteSplittingInspectionServiceTest.java
@@ -17,15 +17,20 @@
package org.apache.shardingsphere.mcp.feature.readwritesplitting.tool.service;
+import
org.apache.shardingsphere.mcp.api.protocol.exception.MCPQueryFailedException;
import
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
import org.junit.jupiter.api.Test;
+import java.sql.SQLException;
+import java.sql.SQLSyntaxErrorException;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -52,7 +57,25 @@ class ReadwriteSplittingInspectionServiceTest {
MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
when(queryFacade.queryWithAnyDatabase("SHOW LOAD BALANCE ALGORITHM
PLUGINS")).thenReturn(List.of(Map.of("type", "WEIGHT")));
List<Map<String, Object>> actual = new
ReadwriteSplittingInspectionService().queryLoadBalanceAlgorithmPlugins(queryFacade);
- assertThat(actual.get(0).get("property_guidance").toString(),
containsString("numeric property"));
+ assertThat(actual.getFirst().get("property_guidance").toString(),
containsString("numeric property"));
+ }
+
+ @Test
+ void assertQueryLoadBalanceAlgorithmPluginsWithUnavailableDistSQL() {
+ MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+ when(queryFacade.queryWithAnyDatabase("SHOW LOAD BALANCE ALGORITHM
PLUGINS"))
+ .thenThrow(new MCPQueryFailedException("syntax error near
'LOAD BALANCE ALGORITHM PLUGINS'", new SQLSyntaxErrorException("syntax
error")));
+ List<Map<String, Object>> actual = new
ReadwriteSplittingInspectionService().queryLoadBalanceAlgorithmPlugins(queryFacade);
+ assertTrue(actual.stream().anyMatch(each ->
"ROUND_ROBIN".equals(each.get("type"))));
+ assertTrue(actual.stream().anyMatch(each ->
"ROUND_ROBIN".equals(each.get("type")) && "No required
properties.".equals(each.get("property_guidance"))));
+ }
+
+ @Test
+ void assertQueryLoadBalanceAlgorithmPluginsPropagatesQueryFailure() {
+ MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+ when(queryFacade.queryWithAnyDatabase("SHOW LOAD BALANCE ALGORITHM
PLUGINS"))
+ .thenThrow(new MCPQueryFailedException("Connection refused.",
new SQLException("Connection refused.")));
+ assertThrows(MCPQueryFailedException.class, () -> new
ReadwriteSplittingInspectionService().queryLoadBalanceAlgorithmPlugins(queryFacade));
}
@Test
diff --git
a/mcp/features/shadow/src/main/java/org/apache/shardingsphere/mcp/feature/shadow/tool/service/ShadowInspectionService.java
b/mcp/features/shadow/src/main/java/org/apache/shardingsphere/mcp/feature/shadow/tool/service/ShadowInspectionService.java
index 06e70f60a36..e21238175c2 100644
---
a/mcp/features/shadow/src/main/java/org/apache/shardingsphere/mcp/feature/shadow/tool/service/ShadowInspectionService.java
+++
b/mcp/features/shadow/src/main/java/org/apache/shardingsphere/mcp/feature/shadow/tool/service/ShadowInspectionService.java
@@ -17,7 +17,9 @@
package org.apache.shardingsphere.mcp.feature.shadow.tool.service;
+import
org.apache.shardingsphere.mcp.api.protocol.exception.MCPQueryFailedException;
import
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
+import
org.apache.shardingsphere.mcp.support.workflow.service.WorkflowDistSQLQueryUtils;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowSQLUtils;
import java.util.LinkedHashMap;
@@ -118,7 +120,18 @@ public final class ShadowInspectionService {
* @return shadow algorithm plugin rows with built-in property guidance
*/
public List<Map<String, Object>> queryAlgorithmPlugins(final
MCPFeatureQueryFacade queryFacade) {
- return queryFacade.queryWithAnyDatabase("SHOW SHADOW ALGORITHM
PLUGINS").stream().map(this::appendPropertyGuidance).toList();
+ return
queryAlgorithmRows(queryFacade).stream().map(this::appendPropertyGuidance).toList();
+ }
+
+ private List<Map<String, Object>> queryAlgorithmRows(final
MCPFeatureQueryFacade queryFacade) {
+ try {
+ return queryFacade.queryWithAnyDatabase("SHOW SHADOW ALGORITHM
PLUGINS");
+ } catch (final MCPQueryFailedException ex) {
+ if
(WorkflowDistSQLQueryUtils.isUnsupportedDistSQLQueryFailure(ex)) {
+ return List.of(Map.of("type", "SQL_HINT"), Map.of("type",
"REGEX_MATCH"), Map.of("type", "VALUE_MATCH"));
+ }
+ throw ex;
+ }
}
private Map<String, Object> appendPropertyGuidance(final Map<String,
Object> row) {
diff --git
a/mcp/features/shadow/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
b/mcp/features/shadow/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
index bef2dae2795..f5970149aa8 100644
---
a/mcp/features/shadow/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
+++
b/mcp/features/shadow/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
@@ -1 +1,18 @@
+#
+# 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.
+#
+
org.apache.shardingsphere.mcp.feature.shadow.completion.ShadowAlgorithmCompletionProvider
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 8422d9450e9..a1f712ffac4 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
@@ -282,11 +282,6 @@ completionTargets:
arguments:
- algorithm_type
maxValues: 50
- - referenceType: resource
- reference: shardingsphere://features/shadow/algorithm-plugins
- arguments:
- - algorithm_type
- maxValues: 50
resourceNavigation:
- from: shardingsphere://features/shadow/databases/{database}/rules
to: database_gateway_plan_shadow_rule
diff --git
a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/descriptor/ShadowToolDescriptorValidatorTest.java
b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/descriptor/ShadowToolDescriptorValidatorTest.java
index 5ab423aeb29..16ef0c16542 100644
---
a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/descriptor/ShadowToolDescriptorValidatorTest.java
+++
b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/descriptor/ShadowToolDescriptorValidatorTest.java
@@ -52,8 +52,7 @@ class ShadowToolDescriptorValidatorTest {
assertThat(rulePromptCompletionTarget.getArguments(),
is(List.of(ShadowFeatureDefinition.ALGORITHM_TYPE_FIELD)));
MCPCompletionTargetDescriptor defaultPromptCompletionTarget =
findCompletionTarget("prompt",
ShadowFeatureDefinition.PLAN_DEFAULT_ALGORITHM_PROMPT_NAME);
assertThat(defaultPromptCompletionTarget.getArguments(),
is(List.of(ShadowFeatureDefinition.ALGORITHM_TYPE_FIELD)));
- MCPCompletionTargetDescriptor resourceCompletionTarget =
findCompletionTarget("resource",
ShadowFeatureDefinition.ALGORITHM_PLUGINS_RESOURCE_URI);
- assertThat(resourceCompletionTarget.getArguments(),
is(List.of(ShadowFeatureDefinition.ALGORITHM_TYPE_FIELD)));
+ assertFalse(hasCompletionTarget("resource",
ShadowFeatureDefinition.ALGORITHM_PLUGINS_RESOURCE_URI));
}
@Test
@@ -99,4 +98,9 @@ class ShadowToolDescriptorValidatorTest {
return
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors().stream()
.filter(each -> referenceType.equals(each.getReferenceType())
&& reference.equals(each.getReference())).findFirst().orElseThrow();
}
+
+ private boolean hasCompletionTarget(final String referenceType, final
String reference) {
+ return
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors().stream()
+ .anyMatch(each ->
referenceType.equals(each.getReferenceType()) &&
reference.equals(each.getReference()));
+ }
}
diff --git
a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/tool/service/ShadowInspectionServiceTest.java
b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/tool/service/ShadowInspectionServiceTest.java
index 565011e484a..8b8324f57f1 100644
---
a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/tool/service/ShadowInspectionServiceTest.java
+++
b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/tool/service/ShadowInspectionServiceTest.java
@@ -17,15 +17,20 @@
package org.apache.shardingsphere.mcp.feature.shadow.tool.service;
+import
org.apache.shardingsphere.mcp.api.protocol.exception.MCPQueryFailedException;
import
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
import org.junit.jupiter.api.Test;
+import java.sql.SQLException;
+import java.sql.SQLSyntaxErrorException;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -59,6 +64,24 @@ class ShadowInspectionServiceTest {
when(queryFacade.queryWithAnyDatabase("SHOW SHADOW ALGORITHM
PLUGINS")).thenReturn(List.of(Map.of("type", "VALUE_MATCH")));
List<Map<String, Object>> actual = new
ShadowInspectionService().queryAlgorithmPlugins(queryFacade);
assertThat(actual.size(), is(1));
- assertThat(String.valueOf(actual.get(0).get("property_guidance")),
containsString("operation"));
+ assertThat(String.valueOf(actual.getFirst().get("property_guidance")),
containsString("operation"));
+ }
+
+ @Test
+ void assertQueryAlgorithmPluginsWithUnavailableDistSQL() {
+ MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+ when(queryFacade.queryWithAnyDatabase("SHOW SHADOW ALGORITHM PLUGINS"))
+ .thenThrow(new MCPQueryFailedException("syntax error near
'SHADOW ALGORITHM PLUGINS'", new SQLSyntaxErrorException("syntax error")));
+ List<Map<String, Object>> actual = new
ShadowInspectionService().queryAlgorithmPlugins(queryFacade);
+ assertTrue(actual.stream().anyMatch(each ->
"VALUE_MATCH".equals(each.get("type"))));
+ assertTrue(actual.stream().anyMatch(each ->
"VALUE_MATCH".equals(each.get("type")) &&
String.valueOf(each.get("property_guidance")).contains("operation")));
+ }
+
+ @Test
+ void assertQueryAlgorithmPluginsPropagatesQueryFailure() {
+ MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+ when(queryFacade.queryWithAnyDatabase("SHOW SHADOW ALGORITHM PLUGINS"))
+ .thenThrow(new MCPQueryFailedException("Connection refused.",
new SQLException("Connection refused.")));
+ assertThrows(MCPQueryFailedException.class, () -> new
ShadowInspectionService().queryAlgorithmPlugins(queryFacade));
}
}
diff --git
a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingInspectionService.java
b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingInspectionService.java
index 0de6254867f..6b2c6962340 100644
---
a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingInspectionService.java
+++
b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingInspectionService.java
@@ -17,7 +17,9 @@
package org.apache.shardingsphere.mcp.feature.sharding.tool.service;
+import
org.apache.shardingsphere.mcp.api.protocol.exception.MCPQueryFailedException;
import
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
+import
org.apache.shardingsphere.mcp.support.workflow.service.WorkflowDistSQLQueryUtils;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowSQLUtils;
import java.util.LinkedHashMap;
@@ -37,7 +39,7 @@ public final class ShardingInspectionService {
* @return sharding algorithm plugin rows
*/
public List<Map<String, Object>> queryAlgorithmPlugins(final
MCPFeatureQueryFacade queryFacade) {
- return queryFacade.queryWithAnyDatabase("SHOW SHARDING ALGORITHM
PLUGINS").stream().map(this::appendShardingAlgorithmGuidance).toList();
+ return queryPluginRows(queryFacade, "SHOW SHARDING ALGORITHM PLUGINS",
getShardingAlgorithmPluginRows()).stream().map(this::appendShardingAlgorithmGuidance).toList();
}
/**
@@ -47,7 +49,36 @@ public final class ShardingInspectionService {
* @return key generate algorithm plugin rows
*/
public List<Map<String, Object>> queryKeyGenerateAlgorithmPlugins(final
MCPFeatureQueryFacade queryFacade) {
- return queryFacade.queryWithAnyDatabase("SHOW KEY GENERATE ALGORITHM
PLUGINS").stream().map(this::appendKeyGeneratorGuidance).toList();
+ return queryPluginRows(queryFacade, "SHOW KEY GENERATE ALGORITHM
PLUGINS",
getKeyGenerateAlgorithmPluginRows()).stream().map(this::appendKeyGeneratorGuidance).toList();
+ }
+
+ private List<Map<String, Object>> queryPluginRows(final
MCPFeatureQueryFacade queryFacade, final String sql, final List<Map<String,
Object>> fallbackRows) {
+ try {
+ return queryFacade.queryWithAnyDatabase(sql);
+ } catch (final MCPQueryFailedException ex) {
+ if
(WorkflowDistSQLQueryUtils.isUnsupportedDistSQLQueryFailure(ex)) {
+ return fallbackRows;
+ }
+ throw ex;
+ }
+ }
+
+ private List<Map<String, Object>> getShardingAlgorithmPluginRows() {
+ return List.of(
+ Map.of("type", "MOD"),
+ Map.of("type", "HASH_MOD"),
+ Map.of("type", "VOLUME_RANGE"),
+ Map.of("type", "BOUNDARY_RANGE"),
+ Map.of("type", "AUTO_INTERVAL"),
+ Map.of("type", "INTERVAL"),
+ Map.of("type", "CLASS_BASED"),
+ Map.of("type", "INLINE"),
+ Map.of("type", "COMPLEX_INLINE"),
+ Map.of("type", "HINT_INLINE"));
+ }
+
+ private List<Map<String, Object>> getKeyGenerateAlgorithmPluginRows() {
+ return List.of(Map.of("type", "SNOWFLAKE"), Map.of("type", "UUID"));
}
/**
diff --git
a/mcp/features/sharding/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
b/mcp/features/sharding/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
index 48b5edcdb99..8ba2f2d5ee3 100644
---
a/mcp/features/sharding/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
+++
b/mcp/features/sharding/src/main/resources/META-INF/services/org.apache.shardingsphere.mcp.support.completion.MCPCompletionProvider
@@ -1 +1,18 @@
+#
+# 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.
+#
+
org.apache.shardingsphere.mcp.feature.sharding.completion.ShardingAlgorithmCompletionProvider
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 252ff929687..e8aa352d1fe 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
@@ -318,7 +318,7 @@ prompts:
description: "Guide the model to plan sharding table rule DistSQL."
binding:
templateResource:
META-INF/shardingsphere-mcp/prompts/plan-sharding-table-rule.md
- arguments: &commonPromptArguments
+ arguments:
- name: database
title: Logical Database
description: "ShardingSphere logical database."
@@ -327,6 +327,10 @@ prompts:
title: Operation Type
description: "create, alter, or drop."
required: false
+ - name: algorithm_type
+ title: Sharding Algorithm
+ description: "Optional sharding algorithm type."
+ required: false
meta:
org.apache.shardingsphere/related-tools:
- database_gateway_plan_sharding_table_rule
@@ -343,7 +347,15 @@ prompts:
description: "Guide the model to plan sharding table reference rule
DistSQL."
binding:
templateResource:
META-INF/shardingsphere-mcp/prompts/plan-sharding-table-reference-rule.md
- arguments: *commonPromptArguments
+ arguments: &commonPromptArguments
+ - name: database
+ title: Logical Database
+ description: "ShardingSphere logical database."
+ required: false
+ - name: operation_type
+ title: Operation Type
+ description: "create, alter, or drop."
+ required: false
meta:
org.apache.shardingsphere/related-tools:
- database_gateway_plan_sharding_table_reference_rule
@@ -360,7 +372,19 @@ prompts:
description: "Guide the model to plan default sharding strategy DistSQL."
binding:
templateResource:
META-INF/shardingsphere-mcp/prompts/plan-sharding-default-strategy.md
- arguments: *commonPromptArguments
+ arguments:
+ - name: database
+ title: Logical Database
+ description: "ShardingSphere logical database."
+ required: false
+ - name: operation_type
+ title: Operation Type
+ description: "create, alter, or drop."
+ required: false
+ - name: algorithm_type
+ title: Sharding Algorithm
+ description: "Optional default sharding algorithm type."
+ required: false
meta:
org.apache.shardingsphere/related-tools:
- database_gateway_plan_sharding_default_strategy
@@ -377,7 +401,19 @@ prompts:
description: "Guide the model to plan sharding key generator DistSQL."
binding:
templateResource:
META-INF/shardingsphere-mcp/prompts/plan-sharding-key-generator.md
- arguments: *commonPromptArguments
+ arguments:
+ - name: database
+ title: Logical Database
+ description: "ShardingSphere logical database."
+ required: false
+ - name: operation_type
+ title: Operation Type
+ description: "create, alter, or drop."
+ required: false
+ - name: key_generator_type
+ title: Key Generator Algorithm
+ description: "Optional key generator algorithm type."
+ required: false
meta:
org.apache.shardingsphere/related-tools:
- database_gateway_plan_sharding_key_generator
@@ -394,7 +430,19 @@ prompts:
description: "Guide the model to plan sharding key generate strategy
DistSQL."
binding:
templateResource:
META-INF/shardingsphere-mcp/prompts/plan-sharding-key-generate-strategy.md
- arguments: *commonPromptArguments
+ arguments:
+ - name: database
+ title: Logical Database
+ description: "ShardingSphere logical database."
+ required: false
+ - name: operation_type
+ title: Operation Type
+ description: "create, alter, or drop."
+ required: false
+ - name: key_generator_type
+ title: Key Generator Algorithm
+ description: "Optional key generator algorithm type."
+ required: false
meta:
org.apache.shardingsphere/related-tools:
- database_gateway_plan_sharding_key_generate_strategy
@@ -424,13 +472,23 @@ prompts:
- "Ask when database, component type, or component name is unclear."
- "Ask before applying generated component cleanup DistSQL that
changes runtime state."
completionTargets:
- - referenceType: resource
- reference: shardingsphere://features/sharding/algorithm-plugins
+ - referenceType: prompt
+ reference: plan_sharding_table_rule
arguments:
- algorithm_type
maxValues: 50
- - referenceType: resource
- reference:
shardingsphere://features/sharding/key-generate-algorithm-plugins
+ - referenceType: prompt
+ reference: plan_sharding_default_strategy
+ arguments:
+ - algorithm_type
+ maxValues: 50
+ - referenceType: prompt
+ reference: plan_sharding_key_generator
+ arguments:
+ - key_generator_type
+ maxValues: 50
+ - referenceType: prompt
+ reference: plan_sharding_key_generate_strategy
arguments:
- key_generator_type
maxValues: 50
diff --git
a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/descriptor/ShardingToolDescriptorValidatorTest.java
b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/descriptor/ShardingToolDescriptorValidatorTest.java
index adf3071d74b..41afa08f8c3 100644
---
a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/descriptor/ShardingToolDescriptorValidatorTest.java
+++
b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/descriptor/ShardingToolDescriptorValidatorTest.java
@@ -53,12 +53,18 @@ class ShardingToolDescriptorValidatorTest {
@Test
void assertExposeCompletionTargets() {
- MCPCompletionTargetDescriptor algorithmCompletionTarget =
findCompletionTarget("resource",
ShardingFeatureDefinition.ALGORITHM_PLUGINS_RESOURCE_URI);
+ MCPCompletionTargetDescriptor algorithmCompletionTarget =
findCompletionTarget("prompt",
ShardingFeatureDefinition.PLAN_TABLE_RULE_PROMPT_NAME);
assertThat(algorithmCompletionTarget.getArguments(),
is(List.of("algorithm_type")));
assertThat(algorithmCompletionTarget.getMaxValues(), is(50));
- MCPCompletionTargetDescriptor keyGeneratorCompletionTarget =
findCompletionTarget("resource",
ShardingFeatureDefinition.KEY_GENERATE_ALGORITHM_PLUGINS_RESOURCE_URI);
+ MCPCompletionTargetDescriptor defaultStrategyCompletionTarget =
findCompletionTarget("prompt",
ShardingFeatureDefinition.PLAN_DEFAULT_STRATEGY_PROMPT_NAME);
+ assertThat(defaultStrategyCompletionTarget.getArguments(),
is(List.of("algorithm_type")));
+ MCPCompletionTargetDescriptor keyGeneratorCompletionTarget =
findCompletionTarget("prompt",
ShardingFeatureDefinition.PLAN_KEY_GENERATOR_PROMPT_NAME);
assertThat(keyGeneratorCompletionTarget.getArguments(),
is(List.of("key_generator_type")));
assertThat(keyGeneratorCompletionTarget.getMaxValues(), is(50));
+ MCPCompletionTargetDescriptor keyGenerateStrategyCompletionTarget =
findCompletionTarget("prompt",
ShardingFeatureDefinition.PLAN_KEY_GENERATE_STRATEGY_PROMPT_NAME);
+ assertThat(keyGenerateStrategyCompletionTarget.getArguments(),
is(List.of("key_generator_type")));
+ assertFalse(hasCompletionTarget("resource",
ShardingFeatureDefinition.ALGORITHM_PLUGINS_RESOURCE_URI));
+ assertFalse(hasCompletionTarget("resource",
ShardingFeatureDefinition.KEY_GENERATE_ALGORITHM_PLUGINS_RESOURCE_URI));
}
@Test
@@ -112,4 +118,9 @@ class ShardingToolDescriptorValidatorTest {
return
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors().stream()
.filter(each -> referenceType.equals(each.getReferenceType())
&& reference.equals(each.getReference())).findFirst().orElseThrow();
}
+
+ private boolean hasCompletionTarget(final String referenceType, final
String reference) {
+ return
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors().stream()
+ .anyMatch(each ->
referenceType.equals(each.getReferenceType()) &&
reference.equals(each.getReference()));
+ }
}
diff --git
a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingInspectionServiceTest.java
b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingInspectionServiceTest.java
index 920f5454fec..3aff51ce69b 100644
---
a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingInspectionServiceTest.java
+++
b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingInspectionServiceTest.java
@@ -17,12 +17,15 @@
package org.apache.shardingsphere.mcp.feature.sharding.tool.service;
+import
org.apache.shardingsphere.mcp.api.protocol.exception.MCPQueryFailedException;
import
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import java.sql.SQLException;
+import java.sql.SQLSyntaxErrorException;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
@@ -30,6 +33,8 @@ import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -50,7 +55,25 @@ class ShardingInspectionServiceTest {
MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
when(queryFacade.queryWithAnyDatabase("SHOW SHARDING ALGORITHM
PLUGINS")).thenReturn(List.of(Map.of("type", "INLINE")));
List<Map<String, Object>> actual = new
ShardingInspectionService().queryAlgorithmPlugins(queryFacade);
- assertThat(actual.get(0).get("property_guidance").toString(),
containsString("algorithm-expression"));
+ assertThat(actual.getFirst().get("property_guidance").toString(),
containsString("algorithm-expression"));
+ }
+
+ @Test
+ void assertQueryAlgorithmPluginsWithUnavailableDistSQL() {
+ MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+ when(queryFacade.queryWithAnyDatabase("SHOW SHARDING ALGORITHM
PLUGINS"))
+ .thenThrow(new MCPQueryFailedException("syntax error near
'SHARDING ALGORITHM PLUGINS'", new SQLSyntaxErrorException("syntax error")));
+ List<Map<String, Object>> actual = new
ShardingInspectionService().queryAlgorithmPlugins(queryFacade);
+ assertTrue(actual.stream().anyMatch(each ->
"INLINE".equals(each.get("type"))));
+ assertTrue(actual.stream().anyMatch(each ->
"INLINE".equals(each.get("type")) &&
String.valueOf(each.get("property_guidance")).contains("algorithm-expression")));
+ }
+
+ @Test
+ void assertQueryAlgorithmPluginsPropagatesQueryFailure() {
+ MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+ when(queryFacade.queryWithAnyDatabase("SHOW SHARDING ALGORITHM
PLUGINS"))
+ .thenThrow(new MCPQueryFailedException("Connection refused.",
new SQLException("Connection refused.")));
+ assertThrows(MCPQueryFailedException.class, () -> new
ShardingInspectionService().queryAlgorithmPlugins(queryFacade));
}
@Test
@@ -58,7 +81,25 @@ class ShardingInspectionServiceTest {
MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
when(queryFacade.queryWithAnyDatabase("SHOW KEY GENERATE ALGORITHM
PLUGINS")).thenReturn(List.of(Map.of("type", "UUID")));
List<Map<String, Object>> actual = new
ShardingInspectionService().queryKeyGenerateAlgorithmPlugins(queryFacade);
- assertThat(actual.get(0).get("property_guidance").toString(), is("No
required properties."));
+ assertThat(actual.getFirst().get("property_guidance").toString(),
is("No required properties."));
+ }
+
+ @Test
+ void assertQueryKeyGenerateAlgorithmPluginsWithUnavailableDistSQL() {
+ MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+ when(queryFacade.queryWithAnyDatabase("SHOW KEY GENERATE ALGORITHM
PLUGINS"))
+ .thenThrow(new MCPQueryFailedException("syntax error near 'KEY
GENERATE ALGORITHM PLUGINS'", new SQLSyntaxErrorException("syntax error")));
+ List<Map<String, Object>> actual = new
ShardingInspectionService().queryKeyGenerateAlgorithmPlugins(queryFacade);
+ assertTrue(actual.stream().anyMatch(each ->
"SNOWFLAKE".equals(each.get("type"))));
+ assertTrue(actual.stream().anyMatch(each ->
"SNOWFLAKE".equals(each.get("type")) &&
String.valueOf(each.get("property_guidance")).contains("worker-id")));
+ }
+
+ @Test
+ void assertQueryKeyGenerateAlgorithmPluginsPropagatesQueryFailure() {
+ MCPFeatureQueryFacade queryFacade = mock(MCPFeatureQueryFacade.class);
+ when(queryFacade.queryWithAnyDatabase("SHOW KEY GENERATE ALGORITHM
PLUGINS"))
+ .thenThrow(new MCPQueryFailedException("Connection refused.",
new SQLException("Connection refused.")));
+ assertThrows(MCPQueryFailedException.class, () -> new
ShardingInspectionService().queryKeyGenerateAlgorithmPlugins(queryFacade));
}
private static Stream<Arguments> assertDatabaseScopedQueryArguments() {
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogValidator.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogValidator.java
index 2c2e671423a..d61ca2bdb05 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogValidator.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogValidator.java
@@ -305,11 +305,12 @@ final class MCPDescriptorCatalogValidator {
Map<String, Set<String>> promptArguments =
prompts.stream().collect(Collectors.toMap(MCPPromptDescriptor::getName,
each ->
each.getArguments().stream().map(MCPPromptArgumentDescriptor::getName).collect(Collectors.toSet())));
Set<String> promptNames = promptArguments.keySet();
- Set<String> resourceUris =
resources.stream().map(MCPResourceDescriptor::getUriTemplate).collect(Collectors.toSet());
+ Map<String, MCPResourceDescriptor> resourceDescriptors =
resources.stream().collect(Collectors.toMap(MCPResourceDescriptor::getUriTemplate,
each -> each));
Map<String, MCPCompletionTargetDescriptor> registered = new
LinkedHashMap<>(descriptors.size(), 1F);
for (MCPCompletionTargetDescriptor each : descriptors) {
- validateCompletionReference(each, promptNames, resourceUris);
+ validateCompletionReference(each, promptNames,
resourceDescriptors.keySet());
validatePromptCompletionArguments(each, promptArguments);
+ validateResourceCompletionArguments(each, resourceDescriptors);
ShardingSpherePreconditions.checkState(null ==
registered.putIfAbsent(each.getReferenceType() + ":" + each.getReference(),
each),
() -> new IllegalStateException(String.format("Duplicate
MCP completion target `%s:%s`.", each.getReferenceType(),
each.getReference())));
}
@@ -326,6 +327,20 @@ final class MCPDescriptorCatalogValidator {
}
}
+ private static void validateResourceCompletionArguments(final
MCPCompletionTargetDescriptor descriptor, final Map<String,
MCPResourceDescriptor> resources) {
+ if (!"resource".equals(descriptor.getReferenceType())) {
+ return;
+ }
+ MCPResourceDescriptor resource =
resources.get(descriptor.getReference());
+ ShardingSpherePreconditions.checkState(resource.isTemplated(),
+ () -> new IllegalStateException(String.format("Completion
target `resource:%s` must reference a resource template.",
descriptor.getReference())));
+ Set<String> templateVariables = new HashSet<>(new
MCPUriTemplate(resource.getUriTemplate()).getVariableNames());
+ for (String each : descriptor.getArguments()) {
+
ShardingSpherePreconditions.checkState(templateVariables.contains(each), () ->
new IllegalStateException(
+ String.format("Completion target `resource:%s` argument
`%s` is not a URI template variable.", descriptor.getReference(), each)));
+ }
+ }
+
private static void validateCompletionReference(final
MCPCompletionTargetDescriptor descriptor, final Set<String> promptNames, final
Set<String> resourceUris) {
if ("prompt".equals(descriptor.getReferenceType())) {
ShardingSpherePreconditions.checkState(promptNames.contains(descriptor.getReference()),
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 bb2e61845b4..f2025f71f9d 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
@@ -101,6 +101,8 @@ class MCPDescriptorCatalogIndexTest {
void assertGetCompletionTargetDescriptors() {
Collection<MCPCompletionTargetDescriptor> actualDescriptors =
MCPDescriptorCatalogIndex.getCompletionTargetDescriptors();
assertTrue(actualDescriptors.stream().anyMatch(each ->
"prompt".equals(each.getReferenceType()) &&
"inspect_metadata".equals(each.getReference())));
+ assertTrue(actualDescriptors.stream().filter(each ->
"resource".equals(each.getReferenceType()))
+ .allMatch(each ->
MCPDescriptorCatalogIndex.getRequiredResourceDescriptor(each.getReference()).isTemplated()));
}
@Test
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportCompletionE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportCompletionE2ETest.java
index 1c75fb92087..55ef7d8cadc 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportCompletionE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportCompletionE2ETest.java
@@ -81,14 +81,19 @@ class HttpTransportCompletionE2ETest extends
AbstractHttpProgrammaticRuntimeE2ET
launchHttpTransport();
HttpClient httpClient = HttpClient.newHttpClient();
String sessionId = initializeSession(httpClient);
- assertTrue(completeValues(httpClient, sessionId,
createResourceReference("shardingsphere://features/readwrite-splitting/load-balance-algorithm-plugins"),
- "load_balancer_type", "ROUND",
Map.of()).contains("ROUND_ROBIN"));
- assertTrue(completeValues(httpClient, sessionId,
createResourceReference("shardingsphere://features/shadow/algorithm-plugins"),
- "algorithm_type", "VALUE", Map.of()).contains("VALUE_MATCH"));
- assertTrue(completeValues(httpClient, sessionId,
createResourceReference("shardingsphere://features/sharding/algorithm-plugins"),
- "algorithm_type", "INLINE", Map.of()).contains("INLINE"));
- assertTrue(completeValues(httpClient, sessionId,
createResourceReference("shardingsphere://features/sharding/key-generate-algorithm-plugins"),
- "key_generator_type", "SNOW", Map.of()).contains("SNOWFLAKE"));
+ Map<String, Object> loadBalanceAlgorithmCompletion =
complete(httpClient, sessionId, Map.of("type", "ref/prompt", "name",
"plan_readwrite_splitting_rule"),
+ "load_balancer_type", "ROUND", Map.of());
+ List<String> loadBalanceAlgorithmValues =
extractCompletionValues(loadBalanceAlgorithmCompletion);
+ assertTrue(loadBalanceAlgorithmValues.contains("ROUND_ROBIN"),
loadBalanceAlgorithmCompletion::toString);
+ List<String> shadowAlgorithmValues = completeValues(httpClient,
sessionId, Map.of("type", "ref/prompt", "name", "plan_shadow_rule"),
+ "algorithm_type", "VALUE", Map.of());
+ assertTrue(shadowAlgorithmValues.contains("VALUE_MATCH"),
shadowAlgorithmValues::toString);
+ List<String> shardingAlgorithmValues = completeValues(httpClient,
sessionId, Map.of("type", "ref/prompt", "name", "plan_sharding_table_rule"),
+ "algorithm_type", "INLINE", Map.of());
+ assertTrue(shardingAlgorithmValues.contains("INLINE"),
shardingAlgorithmValues::toString);
+ List<String> keyGenerateAlgorithmValues = completeValues(httpClient,
sessionId, Map.of("type", "ref/prompt", "name", "plan_sharding_key_generator"),
+ "key_generator_type", "SNOW", Map.of());
+ assertTrue(keyGenerateAlgorithmValues.contains("SNOWFLAKE"),
keyGenerateAlgorithmValues::toString);
}
@Test
@@ -111,7 +116,11 @@ class HttpTransportCompletionE2ETest extends
AbstractHttpProgrammaticRuntimeE2ET
private List<String> completeValues(final HttpClient httpClient, final
String sessionId, final Map<String, Object> reference,
final String argumentName, final
String argumentValue,
final Map<String, String>
contextArguments) throws IOException, InterruptedException {
- Map<String, Object> completion = castToMap(complete(httpClient,
sessionId, reference, argumentName, argumentValue,
contextArguments).get("completion"));
+ return extractCompletionValues(complete(httpClient, sessionId,
reference, argumentName, argumentValue, contextArguments));
+ }
+
+ private List<String> extractCompletionValues(final Map<String, Object>
result) {
+ Map<String, Object> completion = castToMap(result.get("completion"));
return ((List<?>)
completion.get("values")).stream().map(String::valueOf).toList();
}
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 d3fc6d24d88..4c865fa3313 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
@@ -171,9 +171,9 @@ common_flows:
referenced_resources: ['shardingsphere://capabilities']
protocolAvailability: {resources: true, resourceTemplates: true, tools: true,
toolAnnotations: true,
toolOutputSchemas: true, prompts: true, completions: true,
resourceNavigation: true}
-fingerprints: {algorithm: sha256, descriptorCatalog:
0b3cb84bfe1e23964958210cf6ce96c8b613202cd5f5611b7845bb968d558370,
- promptSet: 41a84db849ce19f25f172068e5a176f74fc726429402b9d675d5197acc51d7e2,
resourceNavigation:
64bf1a1ca4856b55a21b09922aa8df6e7bb7f043faa38977680d1a1e3c060bae,
- modelFacingSchemas:
83b4b8d44382cbbfdee351e86dea044690caa90032832ce08bade4dff11777b9}
+fingerprints: {algorithm: sha256, descriptorCatalog:
20e22dc1490af50d39f00aeb1d5fb35c298773096268b70adc898d7f31800d06,
+ promptSet: 63e5b9cb3fada77bc332fd0f958c390d66b538944bdc4ba75501cac5106cd3f3,
resourceNavigation:
64bf1a1ca4856b55a21b09922aa8df6e7bb7f043faa38977680d1a1e3c060bae,
+ modelFacingSchemas:
424743fa39de1312cfc2c19748d2da2773638c31596b747334468b3f130b1067}
resources:
- uri: shardingsphere://capabilities
name: server-capability-catalog
@@ -802,16 +802,19 @@ prompts:
arguments:
- {name: database, required: false}
- {name: operation_type, required: false}
+ - {name: algorithm_type, required: false}
- name: plan_sharding_key_generate_strategy
title: Plan Sharding Key Generate Strategy
arguments:
- {name: database, required: false}
- {name: operation_type, required: false}
+ - {name: key_generator_type, required: false}
- name: plan_sharding_key_generator
title: Plan Sharding Key Generator
arguments:
- {name: database, required: false}
- {name: operation_type, required: false}
+ - {name: key_generator_type, required: false}
- name: plan_sharding_rule_component_cleanup
title: Plan Sharding Rule Component Cleanup
arguments:
@@ -827,6 +830,7 @@ prompts:
arguments:
- {name: database, required: false}
- {name: operation_type, required: false}
+ - {name: algorithm_type, required: false}
- name: recover_workflow
title: Recover Workflow
arguments:
@@ -887,18 +891,38 @@ completionTargets:
reference: plan_encrypt_rule
arguments: [algorithm_type, assisted_query_algorithm_type,
like_query_algorithm_type]
maxValues: 50
-- referenceType: resource
- reference: shardingsphere://features/encrypt/algorithms
- arguments: [algorithm_type, assisted_query_algorithm_type,
like_query_algorithm_type]
- maxValues: 50
- referenceType: prompt
reference: plan_mask_rule
arguments: [algorithm_type]
maxValues: 50
-- referenceType: resource
- reference: shardingsphere://features/mask/algorithms
+- referenceType: prompt
+ reference: plan_readwrite_splitting_rule
+ arguments: [load_balancer_type]
+ maxValues: 50
+- referenceType: prompt
+ reference: plan_shadow_rule
+ arguments: [algorithm_type]
+ maxValues: 50
+- referenceType: prompt
+ reference: plan_default_shadow_algorithm
+ arguments: [algorithm_type]
+ maxValues: 50
+- referenceType: prompt
+ reference: plan_sharding_table_rule
arguments: [algorithm_type]
maxValues: 50
+- referenceType: prompt
+ reference: plan_sharding_default_strategy
+ arguments: [algorithm_type]
+ maxValues: 50
+- referenceType: prompt
+ reference: plan_sharding_key_generator
+ arguments: [key_generator_type]
+ maxValues: 50
+- referenceType: prompt
+ reference: plan_sharding_key_generate_strategy
+ arguments: [key_generator_type]
+ maxValues: 50
- referenceType: prompt
reference: inspect_metadata
arguments: [database, schema]