You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/05/26 01:22:07 UTC
(shardingsphere) branch master updated: Refactor mcp module (#38719)
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 626076adf89 Refactor mcp module (#38719)
626076adf89 is described below
commit 626076adf89fbad34299c91e1e8568edaa882b88
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Tue May 26 09:21:59 2026 +0800
Refactor mcp module (#38719)
* Refactor mcp module
* Refactor mcp module
* Refactor mcp module
* Refactor mcp module
* Refactor mcp module
---
...YamlRuntimeDatabaseConfigurationsValidator.java | 38 ++--
.../capability/tool/MCPCallToolResultFactory.java | 8 +-
.../tool/MCPResourceLinkCandidateCollector.java | 40 ++--
.../prompt/MCPPromptSpecificationFactoryTest.java | 4 +-
.../core/handler/core/CoreResourceHandlers.java | 43 ++--
.../handler/metadata/MetadataResourceHandler.java | 8 +-
.../mcp/core/tool/MCPToolCallLimiter.java | 6 +-
.../execute/SQLStatementTargetResolver.java | 114 +++++------
.../handler/metadata/MetadataSearchCollector.java | 6 +-
.../metadata/SearchMetadataToolHandler.java | 6 +-
.../WorkflowRuntimeDefinitionRegistry.java | 10 +-
.../handler/CoreResourceHandlerSurfaceTest.java | 43 ++--
.../handler/execute/MCPSQLExecutionFacadeTest.java | 62 +++---
.../tool/service/DerivedColumnNamingService.java | 5 +-
.../EncryptAlgorithmRecommendationService.java | 8 +-
.../service/EncryptWorkflowValidationService.java | 2 +-
.../jdbc/MCPJdbcDatabaseProfileLoader.java | 46 ++---
.../metadata/jdbc/MCPJdbcMetadataLoader.java | 25 +--
.../descriptor/MCPDescriptorCatalogValidator.java | 9 +-
.../mcp/support/protocol/MCPResourceHintUtils.java | 2 +-
.../service/WorkflowGuidancePayloadBuilder.java | 46 ++---
.../workflow/service/WorkflowRequestBinder.java | 10 +-
.../support/workflow/service/WorkflowSQLUtils.java | 6 +-
.../service/WorkflowValidationSupport.java | 4 +-
.../metadata/jdbc/MCPJdbcMetadataLoaderTest.java | 4 +-
.../security/MCPClientSafetyPolicyTest.java | 6 +-
.../service/WorkflowPlanningSupportTest.java | 5 +-
.../e2e/mcp/llm/config/LLME2EConfiguration.java | 6 +-
.../mcp/llm/conversation/LLMMCPActionExecutor.java | 8 +-
.../llm/conversation/LLMMCPConversationRunner.java | 148 +++++++------
.../LLMMCPConversationRunnerCoverageTest.java | 185 ++++++++---------
.../LLMMCPConversationRunnerFailureTest.java | 152 +++++++-------
.../LLMMCPConversationRunnerNextActionTest.java | 200 +++++++++---------
.../conversation/LLMMCPConversationRunnerTest.java | 228 +++++++++++----------
.../LLMMCPModelFacingToolResponseFormatter.java | 34 +--
.../mcp/llm/conversation/LLMMCPNextActions.java | 4 +-
.../llm/conversation/LLMMCPScenarioInference.java | 12 +-
.../conversation/LLMMCPSideEffectNextAction.java | 6 +-
.../llm/conversation/LLMMCPToolCallNormalizer.java | 40 ++--
.../e2e/mcp/llm/scenario/LLMStructuredAnswer.java | 4 +-
.../suite/MCPBuilderEvaluationArtifactTest.java | 11 +-
.../suite/usability/LLMUsabilitySuiteRunner.java | 10 +-
.../assessment/LLMUsabilityMetricCalculator.java | 2 +-
.../assessment/LLMUsabilityReportWriter.java | 8 +-
.../AbstractProductionMySQLRuntimeE2ETest.java | 9 +-
.../production/PackagedDistributionE2ETest.java | 7 +-
.../production/ProductionMySQLRuntimeE2ETest.java | 7 +-
.../programmatic/HttpTransportContractE2ETest.java | 5 +-
.../PackagedDistributionTestSupportTest.java | 37 ++--
.../support/runtime/MySQLRuntimeTestSupport.java | 6 +-
.../runtime/MySQLRuntimeTestSupportTest.java | 6 +-
51 files changed, 858 insertions(+), 843 deletions(-)
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/config/yaml/validator/YamlRuntimeDatabaseConfigurationsValidator.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/config/yaml/validator/YamlRuntimeDatabaseConfigurationsValidator.java
index 324cd88aa05..16c96059fb2 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/config/yaml/validator/YamlRuntimeDatabaseConfigurationsValidator.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/config/yaml/validator/YamlRuntimeDatabaseConfigurationsValidator.java
@@ -41,47 +41,47 @@ public final class YamlRuntimeDatabaseConfigurationsValidator implements Constra
return result;
}
- private boolean validateRuntimeDatabase(final Entry<String, Map<String, Object>> entry, final ConstraintValidatorContext context) {
- if (null == entry.getValue()) {
- addViolation(context, String.format("contains null configuration for database `%s`", entry.getKey()));
+ private boolean validateRuntimeDatabase(final Entry<String, Map<String, Object>> databaseEntry, final ConstraintValidatorContext context) {
+ if (null == databaseEntry.getValue()) {
+ addViolation(context, String.format("contains null configuration for database `%s`", databaseEntry.getKey()));
return false;
}
- return validateSupportedProperties(entry, context) && validateRequiredProperties(entry, context);
+ return validateSupportedProperties(databaseEntry, context) && validateRequiredProperties(databaseEntry, context);
}
- private boolean validateSupportedProperties(final Entry<String, Map<String, Object>> entry, final ConstraintValidatorContext context) {
+ private boolean validateSupportedProperties(final Entry<String, Map<String, Object>> databaseEntry, final ConstraintValidatorContext context) {
boolean result = true;
- for (String each : entry.getValue().keySet()) {
+ for (String each : databaseEntry.getValue().keySet()) {
if (!YamlRuntimeDatabaseConfigurationProperties.SUPPORTED_PROPERTIES.contains(each)) {
- addViolation(context, String.format("contains unsupported property `%s` for database `%s`", each, entry.getKey()));
+ addViolation(context, String.format("contains unsupported property `%s` for database `%s`", each, databaseEntry.getKey()));
result = false;
}
}
return result;
}
- private boolean validateRequiredProperties(final Entry<String, Map<String, Object>> entry, final ConstraintValidatorContext context) {
- boolean result = validateRequiredText(entry, YamlRuntimeDatabaseConfigurationProperties.DATABASE_TYPE, context);
- result = validateRequiredText(entry, YamlRuntimeDatabaseConfigurationProperties.JDBC_URL, context) && result;
- result = validateExplicitText(entry, YamlRuntimeDatabaseConfigurationProperties.USERNAME, context) && result;
- result = validateExplicitText(entry, YamlRuntimeDatabaseConfigurationProperties.PASSWORD, context) && result;
- return validateExplicitText(entry, YamlRuntimeDatabaseConfigurationProperties.DRIVER_CLASS_NAME, context) && result;
+ private boolean validateRequiredProperties(final Entry<String, Map<String, Object>> databaseEntry, final ConstraintValidatorContext context) {
+ boolean result = validateRequiredText(databaseEntry, YamlRuntimeDatabaseConfigurationProperties.DATABASE_TYPE, context);
+ result = validateRequiredText(databaseEntry, YamlRuntimeDatabaseConfigurationProperties.JDBC_URL, context) && result;
+ result = validateExplicitText(databaseEntry, YamlRuntimeDatabaseConfigurationProperties.USERNAME, context) && result;
+ result = validateExplicitText(databaseEntry, YamlRuntimeDatabaseConfigurationProperties.PASSWORD, context) && result;
+ return validateExplicitText(databaseEntry, YamlRuntimeDatabaseConfigurationProperties.DRIVER_CLASS_NAME, context) && result;
}
- private boolean validateRequiredText(final Entry<String, Map<String, Object>> entry, final String key, final ConstraintValidatorContext context) {
- Object value = entry.getValue().get(key);
+ private boolean validateRequiredText(final Entry<String, Map<String, Object>> databaseEntry, final String key, final ConstraintValidatorContext context) {
+ Object value = databaseEntry.getValue().get(key);
if (null != value && !value.toString().isBlank()) {
return true;
}
- addViolation(context, String.format("contains database `%s` property `%s` is required", entry.getKey(), key));
+ addViolation(context, String.format("contains database `%s` property `%s` is required", databaseEntry.getKey(), key));
return false;
}
- private boolean validateExplicitText(final Entry<String, Map<String, Object>> entry, final String key, final ConstraintValidatorContext context) {
- if (null != entry.getValue().get(key)) {
+ private boolean validateExplicitText(final Entry<String, Map<String, Object>> databaseEntry, final String key, final ConstraintValidatorContext context) {
+ if (null != databaseEntry.getValue().get(key)) {
return true;
}
- addViolation(context, String.format("contains database `%s` property `%s` is required. Use an empty string when no value is needed", entry.getKey(), key));
+ addViolation(context, String.format("contains database `%s` property `%s` is required. Use an empty string when no value is needed", databaseEntry.getKey(), key));
return false;
}
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPCallToolResultFactory.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPCallToolResultFactory.java
index 8ce258da4c1..35ef597dda7 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPCallToolResultFactory.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPCallToolResultFactory.java
@@ -40,11 +40,11 @@ final class MCPCallToolResultFactory {
if (response instanceof MCPErrorResponse) {
return create(response.toPayload(), true);
}
- final Map<String, Object> payload = response.toPayload();
+ Map<String, Object> payload = response.toPayload();
if (descriptor.getOutputSchema().isEmpty()) {
return create(payload, false);
}
- final ValidationResponse validation = outputSchemaValidator.validate(descriptor.getOutputSchema(), payload);
+ ValidationResponse validation = outputSchemaValidator.validate(descriptor.getOutputSchema(), payload);
return validation.valid()
? create(payload, false)
: create(new MCPErrorResponse(String.format(
@@ -53,13 +53,13 @@ final class MCPCallToolResultFactory {
}
private CallToolResult create(final Map<String, Object> payload, final boolean isError) {
- final CallToolResult.Builder result = CallToolResult.builder().structuredContent(payload).addTextContent(JsonUtils.toJsonString(payload)).isError(isError);
+ CallToolResult.Builder result = CallToolResult.builder().structuredContent(payload).addTextContent(JsonUtils.toJsonString(payload)).isError(isError);
appendResourceLinks(payload, result);
return result.build();
}
private void appendResourceLinks(final Map<String, Object> payload, final CallToolResult.Builder builder) {
- final MCPResourceLinkCandidateCollector.ResourceLinkCandidates candidates = new MCPResourceLinkCandidateCollector(RESOURCE_LINK_LIMIT).collect(payload);
+ MCPResourceLinkCandidateCollector.ResourceLinkCandidates candidates = new MCPResourceLinkCandidateCollector(RESOURCE_LINK_LIMIT).collect(payload);
int emittedCount = 0;
for (MCPResourceLinkCandidateCollector.ResourceLinkCandidate each : candidates.candidates()) {
builder.addContent(MCPResourceLinkContract.createResourceLink(each));
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkCandidateCollector.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkCandidateCollector.java
index 88a5b7a3187..d33697a80a5 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkCandidateCollector.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkCandidateCollector.java
@@ -63,30 +63,30 @@ final class MCPResourceLinkCandidateCollector {
return new ResourceLinkCandidates(createLimitedCandidates(uniqueCandidates), uniqueCandidates.size());
}
- private void collectResourceLinkCandidates(final Map<?, ?> payload, final List<OrderedResourceLinkCandidate> result) {
- collectResourceLinkFields(payload, result);
- collectRecoveryResourceLinkFields(payload, result);
- collectItemResourceLinkFields(payload, result);
+ private void collectResourceLinkCandidates(final Map<?, ?> payload, final List<OrderedResourceLinkCandidate> candidates) {
+ collectResourceLinkFields(payload, candidates);
+ collectRecoveryResourceLinkFields(payload, candidates);
+ collectItemResourceLinkFields(payload, candidates);
}
- private void collectResourceLinkFields(final Map<?, ?> value, final List<OrderedResourceLinkCandidate> result) {
- collectResourceLinkValue(value.get(RESOURCES_TO_READ_FIELD), RESOURCES_TO_READ_FIELD, result);
- collectResourceLinkValue(value.get(RESOURCE_FIELD), RESOURCE_FIELD, result);
- collectResourceLinkValue(value.get(PARENT_RESOURCE_FIELD), PARENT_RESOURCE_FIELD, result);
- collectResourceLinkValue(value.get(NEXT_RESOURCES_FIELD), NEXT_RESOURCES_FIELD, result);
+ private void collectResourceLinkFields(final Map<?, ?> value, final List<OrderedResourceLinkCandidate> candidates) {
+ collectResourceLinkValue(value.get(RESOURCES_TO_READ_FIELD), RESOURCES_TO_READ_FIELD, candidates);
+ collectResourceLinkValue(value.get(RESOURCE_FIELD), RESOURCE_FIELD, candidates);
+ collectResourceLinkValue(value.get(PARENT_RESOURCE_FIELD), PARENT_RESOURCE_FIELD, candidates);
+ collectResourceLinkValue(value.get(NEXT_RESOURCES_FIELD), NEXT_RESOURCES_FIELD, candidates);
}
- private void collectResourceLinkValue(final Object value, final String sourceField, final List<OrderedResourceLinkCandidate> result) {
+ private void collectResourceLinkValue(final Object value, final String sourceField, final List<OrderedResourceLinkCandidate> candidates) {
if (value instanceof Map<?, ?> resourceLinkValue) {
- collectResourceLinkHint(resourceLinkValue, sourceField, result);
+ collectResourceLinkHint(resourceLinkValue, sourceField, candidates);
} else if (value instanceof Iterable<?> resourceLinkValues) {
- collectIterableResourceLinkHints(resourceLinkValues, sourceField, result);
+ collectIterableResourceLinkHints(resourceLinkValues, sourceField, candidates);
}
}
- private void collectResourceLinkHint(final Map<?, ?> value, final String sourceField, final List<OrderedResourceLinkCandidate> result) {
+ private void collectResourceLinkHint(final Map<?, ?> value, final String sourceField, final List<OrderedResourceLinkCandidate> candidates) {
if (isResourceHint(value)) {
- result.add(createCandidate(value, sourceField, result.size()));
+ candidates.add(createCandidate(value, sourceField, candidates.size()));
}
}
@@ -110,29 +110,29 @@ final class MCPResourceLinkCandidateCollector {
return result.isEmpty() ? sourceField : result;
}
- private void collectIterableResourceLinkHints(final Iterable<?> value, final String sourceField, final List<OrderedResourceLinkCandidate> result) {
+ private void collectIterableResourceLinkHints(final Iterable<?> value, final String sourceField, final List<OrderedResourceLinkCandidate> candidates) {
for (Object each : value) {
if (each instanceof Map<?, ?> resourceLinkValue) {
- collectResourceLinkHint(resourceLinkValue, sourceField, result);
+ collectResourceLinkHint(resourceLinkValue, sourceField, candidates);
}
}
}
- private void collectRecoveryResourceLinkFields(final Map<?, ?> payload, final List<OrderedResourceLinkCandidate> result) {
+ private void collectRecoveryResourceLinkFields(final Map<?, ?> payload, final List<OrderedResourceLinkCandidate> candidates) {
Object recovery = payload.get(RECOVERY_FIELD);
if (recovery instanceof Map<?, ?> recoveryPayload) {
- collectResourceLinkFields(recoveryPayload, result);
+ collectResourceLinkFields(recoveryPayload, candidates);
}
}
- private void collectItemResourceLinkFields(final Map<?, ?> payload, final List<OrderedResourceLinkCandidate> result) {
+ private void collectItemResourceLinkFields(final Map<?, ?> payload, final List<OrderedResourceLinkCandidate> candidates) {
Object items = payload.get(ITEMS_FIELD);
if (!(items instanceof Iterable<?> itemValues)) {
return;
}
for (Object each : itemValues) {
if (each instanceof Map<?, ?> item) {
- collectResourceLinkFields(item, result);
+ collectResourceLinkFields(item, candidates);
}
}
}
diff --git a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/prompt/MCPPromptSpecificationFactoryTest.java b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/prompt/MCPPromptSpecificationFactoryTest.java
index a18f1f188e0..f8de64ee058 100644
--- a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/prompt/MCPPromptSpecificationFactoryTest.java
+++ b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/prompt/MCPPromptSpecificationFactoryTest.java
@@ -152,8 +152,8 @@ class MCPPromptSpecificationFactoryTest {
return promptSpecification.promptHandler().apply(mock(McpSyncServerExchange.class), new McpSchema.GetPromptRequest(name, arguments));
}
- private String readText(final McpSchema.GetPromptResult result) {
- return ((McpSchema.TextContent) result.messages().get(0).content()).text();
+ private String readText(final McpSchema.GetPromptResult promptResult) {
+ return ((McpSchema.TextContent) promptResult.messages().get(0).content()).text();
}
private void assertRenderedLines(final String text, final List<String> expectedLines) {
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreResourceHandlers.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreResourceHandlers.java
index 121cbba32f3..6377393fba4 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreResourceHandlers.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreResourceHandlers.java
@@ -32,7 +32,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-import java.util.Optional;
import java.util.function.BiFunction;
/**
@@ -52,22 +51,23 @@ final class CoreResourceHandlers {
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryDatabases()));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}",
- (requestContext, uriVariables) -> singletonOrEmpty(
- requestContext.getMetadataQueryFacade().queryDatabase(uriVariables.getValue("database")))));
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryDatabase(uriVariables.getValue("database"))
+ .map(CoreResourceHandlers::createSingletonList).orElse(Collections.emptyList())));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas",
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().querySchemas(uriVariables.getValue("database"))));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}",
- (requestContext, uriVariables) -> singletonOrEmpty(
- requestContext.getMetadataQueryFacade().querySchema(uriVariables.getValue("database"), uriVariables.getValue("schema")))));
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().querySchema(uriVariables.getValue("database"), uriVariables.getValue("schema"))
+ .map(CoreResourceHandlers::createSingletonList).orElse(Collections.emptyList())));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/sequences",
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().querySequences(uriVariables.getValue("database"), uriVariables.getValue("schema"))));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/sequences/{sequence}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().querySequence(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("sequence")))));
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().querySequence(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("sequence"))
+ .map(CoreResourceHandlers::createSingletonList).orElse(Collections.emptyList())));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/tables",
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryTables(uriVariables.getValue("database"), uriVariables.getValue("schema"))));
@@ -76,41 +76,46 @@ final class CoreResourceHandlers {
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryViews(uriVariables.getValue("database"), uriVariables.getValue("schema"))));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryTable(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table")))));
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryTable(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"))
+ .map(CoreResourceHandlers::createSingletonList).orElse(Collections.emptyList())));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/columns",
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryTableColumns(
uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"))));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/columns/{column}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryTableColumn(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"), uriVariables.getValue("column")))));
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryTableColumn(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"), uriVariables.getValue("column"))
+ .map(CoreResourceHandlers::createSingletonList).orElse(Collections.emptyList())));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/views/{view}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryView(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view")))));
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryView(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view"))
+ .map(CoreResourceHandlers::createSingletonList).orElse(Collections.emptyList())));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/views/{view}/columns",
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryViewColumns(
uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view"))));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/views/{view}/columns/{column}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryViewColumn(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view"), uriVariables.getValue("column")))));
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryViewColumn(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view"), uriVariables.getValue("column"))
+ .map(CoreResourceHandlers::createSingletonList).orElse(Collections.emptyList())));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/indexes",
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryIndexes(
uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"))));
result.add(createMetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/indexes/{index}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryIndex(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"), uriVariables.getValue("index")))));
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryIndex(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"), uriVariables.getValue("index"))
+ .map(CoreResourceHandlers::createSingletonList).orElse(Collections.emptyList())));
return result;
}
- private static List<?> singletonOrEmpty(final Optional<?> metadata) {
- return metadata.map(Collections::singletonList).orElse(Collections.emptyList());
+ private static List<?> createSingletonList(final Object metadata) {
+ return Collections.singletonList(metadata);
}
private static MetadataResourceHandler createMetadataResourceHandler(final String uriTemplate,
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandler.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandler.java
index d3cbee53e3d..1a216c8aa32 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandler.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandler.java
@@ -218,7 +218,7 @@ public final class MetadataResourceHandler implements MCPResourceHandler<MCPData
String uriOrTemplate = descriptor.getUriTemplate();
Optional<String> selfUri = new MCPUriTemplate(uriOrTemplate).expandIfComplete(uriVariables);
selfUri.ifPresent(uri -> result.put("self_uri", uri));
- String parentUri = createParentUri(selfUri);
+ String parentUri = createParentUri(selfUri.orElse(""));
if (!parentUri.isEmpty()) {
result.put("parent_resource", MCPResourceHintUtils.create(parentUri, resolveResourceKind(parentUri), "inspect_parent",
"Read the parent metadata resource before broadening or correcting the request.", "parent_resource"));
@@ -259,15 +259,15 @@ public final class MetadataResourceHandler implements MCPResourceHandler<MCPData
return "logical-database";
}
- private String createParentUri(final Optional<String> selfUri) {
+ private String createParentUri(final String selfUri) {
if (selfUri.isEmpty()) {
return "";
}
String prefix = "shardingsphere://";
- if (!selfUri.get().startsWith(prefix)) {
+ if (!selfUri.startsWith(prefix)) {
return "";
}
- String path = selfUri.get().substring(prefix.length());
+ String path = selfUri.substring(prefix.length());
int lastSeparatorIndex = path.lastIndexOf('/');
return 0 > lastSeparatorIndex ? "" : prefix + path.substring(0, lastSeparatorIndex);
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/MCPToolCallLimiter.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/MCPToolCallLimiter.java
index b8493c2f6af..9d9104b3664 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/MCPToolCallLimiter.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/MCPToolCallLimiter.java
@@ -46,8 +46,8 @@ public final class MCPToolCallLimiter {
* @throws MCPToolCallLimitExceededException when session tool call quota is exhausted
*/
public void acquire(final String sessionId, final String toolName) {
- final String actualSessionId = normalizeSessionId(sessionId);
- final int callCount = sessionToolCallCounts.computeIfAbsent(actualSessionId, ignored -> new AtomicInteger()).incrementAndGet();
+ String actualSessionId = normalizeSessionId(sessionId);
+ int callCount = sessionToolCallCounts.computeIfAbsent(actualSessionId, ignored -> new AtomicInteger()).incrementAndGet();
if (callCount > maxToolCallsPerSession) {
throw new MCPToolCallLimitExceededException(actualSessionId, Objects.toString(toolName, ""), maxToolCallsPerSession);
}
@@ -63,7 +63,7 @@ public final class MCPToolCallLimiter {
}
private String normalizeSessionId(final String sessionId) {
- final String result = Objects.toString(sessionId, "").trim();
+ String result = Objects.toString(sessionId, "").trim();
return result.isEmpty() ? "anonymous" : result;
}
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementTargetResolver.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementTargetResolver.java
index babc0ce4201..a60ac1d33f2 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementTargetResolver.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementTargetResolver.java
@@ -54,50 +54,50 @@ final class SQLStatementTargetResolver {
return result;
}
- private void collect(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> result) {
+ private void collect(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames) {
if ("SELECT".equals(statementStructure.statementType())) {
- collectCommonTableExpressionObjectNames(statementStructure, visitedAliases, result);
- collectSelectTargetObjectNames(statementStructure, visitedAliases, result);
+ collectCommonTableExpressionObjectNames(statementStructure, visitedAliases, objectNames);
+ collectSelectTargetObjectNames(statementStructure, visitedAliases, objectNames);
return;
}
- collectDirectTargetObjectNames(statementStructure, visitedAliases, result);
- collectClauseObjectNames(statementStructure, visitedAliases, result, getClauseKeywords(statementStructure.statementType()));
- collectCommonTableExpressionObjectNames(statementStructure, visitedAliases, result);
+ collectDirectTargetObjectNames(statementStructure, visitedAliases, objectNames);
+ collectClauseObjectNames(statementStructure, visitedAliases, objectNames, getClauseKeywords(statementStructure.statementType()));
+ collectCommonTableExpressionObjectNames(statementStructure, visitedAliases, objectNames);
}
- private void collectCommonTableExpressionObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> result) {
+ private void collectCommonTableExpressionObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames) {
for (SQLCommonTableExpression each : statementStructure.commonTableExpressions()) {
String normalizedAliasName = scanner.normalizeIdentifierForComparison(each.aliasName());
if (visitedAliases.contains(normalizedAliasName)) {
continue;
}
- collect(each.statementStructure(), appendVisitedAlias(visitedAliases, normalizedAliasName), result);
+ collect(each.statementStructure(), appendVisitedAlias(visitedAliases, normalizedAliasName), objectNames);
}
}
- private void collectSelectTargetObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> result) {
- collectClauseObjectNames(statementStructure, visitedAliases, result, "FROM", "JOIN");
+ private void collectSelectTargetObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames) {
+ collectClauseObjectNames(statementStructure, visitedAliases, objectNames, "FROM", "JOIN");
}
- private void collectClauseObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> result, final String... keywords) {
+ private void collectClauseObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames, final String... keywords) {
List<SQLStatementToken> tokens = scanner.tokenize(statementStructure.mainSql());
for (int each : findKeywordIndexes(tokens, keywords)) {
- collectObjectNamesFromClause(statementStructure, tokens, each, visitedAliases, result);
+ collectObjectNamesFromClause(statementStructure, tokens, each, visitedAliases, objectNames);
}
- collectNestedQueryObjectNames(tokens, visitedAliases, result);
- collectQualifiedFunctionNames(tokens, result);
+ collectNestedQueryObjectNames(tokens, visitedAliases, objectNames);
+ collectQualifiedFunctionNames(tokens, objectNames);
}
private void collectObjectNamesFromClause(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final int keywordIndex,
- final Collection<String> visitedAliases, final Collection<String> result) {
+ final Collection<String> visitedAliases, final Collection<String> objectNames) {
int objectStartIndex = keywordIndex + 1;
if (scanner.isKeyword(tokens.get(keywordIndex), "INHERITS") && objectStartIndex < tokens.size() && "(".equals(tokens.get(objectStartIndex).text())) {
objectStartIndex++;
}
- collectObjectNamesFromList(statementStructure, tokens, objectStartIndex, visitedAliases, result, "ONLY", "LATERAL");
+ collectObjectNamesFromList(statementStructure, tokens, objectStartIndex, visitedAliases, objectNames, "ONLY", "LATERAL");
}
- private void collectNestedQueryObjectNames(final List<SQLStatementToken> tokens, final Collection<String> visitedAliases, final Collection<String> result) {
+ private void collectNestedQueryObjectNames(final List<SQLStatementToken> tokens, final Collection<String> visitedAliases, final Collection<String> objectNames) {
int index = 0;
while (index < tokens.size()) {
if (!"(".equals(tokens.get(index).text())) {
@@ -107,7 +107,7 @@ final class SQLStatementTargetResolver {
int closingParenthesisIndex = findClosingParenthesis(tokens, index);
if (isNestedQuery(tokens, index + 1, closingParenthesisIndex)) {
SQLStatementStructure statementStructure = structureResolver.resolve(reconstructSql(tokens, index + 1, closingParenthesisIndex));
- collect(statementStructure, visitedAliases, result);
+ collect(statementStructure, visitedAliases, objectNames);
index = closingParenthesisIndex + 1;
continue;
}
@@ -115,67 +115,67 @@ final class SQLStatementTargetResolver {
}
}
- private void collectObjectName(final SQLStatementStructure statementStructure, final String objectName, final Collection<String> visitedAliases, final Collection<String> result) {
+ private void collectObjectName(final SQLStatementStructure statementStructure, final String objectName, final Collection<String> visitedAliases, final Collection<String> objectNames) {
Optional<SQLCommonTableExpression> commonTableExpression = findCommonTableExpression(statementStructure, objectName);
if (commonTableExpression.isEmpty()) {
- addObjectName(result, objectName);
+ addObjectName(objectNames, objectName);
return;
}
SQLCommonTableExpression actualCommonTableExpression = commonTableExpression.get();
String normalizedAliasName = scanner.normalizeIdentifierForComparison(actualCommonTableExpression.aliasName());
if (!visitedAliases.contains(normalizedAliasName)) {
- collect(actualCommonTableExpression.statementStructure(), appendVisitedAlias(visitedAliases, normalizedAliasName), result);
+ collect(actualCommonTableExpression.statementStructure(), appendVisitedAlias(visitedAliases, normalizedAliasName), objectNames);
}
}
- private void collectDirectTargetObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> result) {
+ private void collectDirectTargetObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames) {
List<SQLStatementToken> tokens = scanner.tokenize(statementStructure.mainSql());
String statementType = statementStructure.statementType();
if ("INSERT".equals(statementType) || "MERGE".equals(statementType)) {
- collectObjectNamesAfterKeyword(statementStructure, tokens, "INTO", visitedAliases, result);
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "INTO", visitedAliases, objectNames);
return;
}
if ("UPDATE".equals(statementType)) {
- collectObjectNamesAfterKeyword(statementStructure, tokens, "UPDATE", visitedAliases, result, "ONLY");
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "UPDATE", visitedAliases, objectNames, "ONLY");
return;
}
if ("DELETE".equals(statementType)) {
- collectDeleteTargetObjectNames(statementStructure, tokens, visitedAliases, result);
+ collectDeleteTargetObjectNames(statementStructure, tokens, visitedAliases, objectNames);
return;
}
if ("CREATE".equals(statementType)) {
- collectObjectNamesAfterTypeKeyword(statementStructure, tokens, visitedAliases, result, "CONCURRENTLY", "IF", "NOT", "EXISTS");
- collectCreateSourceObjectNames(statementStructure, tokens, visitedAliases, result);
+ collectObjectNamesAfterTypeKeyword(statementStructure, tokens, visitedAliases, objectNames, "CONCURRENTLY", "IF", "NOT", "EXISTS");
+ collectCreateSourceObjectNames(statementStructure, tokens, visitedAliases, objectNames);
return;
}
if ("ALTER".equals(statementType)) {
- collectObjectNamesAfterTypeKeyword(statementStructure, tokens, visitedAliases, result, "IF", "EXISTS", "ONLY");
- collectAlterDestinationObjectNames(statementStructure, tokens, visitedAliases, result);
+ collectObjectNamesAfterTypeKeyword(statementStructure, tokens, visitedAliases, objectNames, "IF", "EXISTS", "ONLY");
+ collectAlterDestinationObjectNames(statementStructure, tokens, visitedAliases, objectNames);
return;
}
if ("DROP".equals(statementType)) {
- collectObjectNamesAfterTypeKeyword(statementStructure, tokens, visitedAliases, result, "CONCURRENTLY", "IF", "EXISTS");
- collectDropSourceObjectNames(statementStructure, tokens, visitedAliases, result);
+ collectObjectNamesAfterTypeKeyword(statementStructure, tokens, visitedAliases, objectNames, "CONCURRENTLY", "IF", "EXISTS");
+ collectDropSourceObjectNames(statementStructure, tokens, visitedAliases, objectNames);
return;
}
if ("TRUNCATE".equals(statementType)) {
- if (!collectObjectNamesAfterKeyword(statementStructure, tokens, "TABLE", visitedAliases, result, "ONLY")) {
- collectObjectNamesFromList(statementStructure, tokens, 1, visitedAliases, result, "ONLY");
+ if (!collectObjectNamesAfterKeyword(statementStructure, tokens, "TABLE", visitedAliases, objectNames, "ONLY")) {
+ collectObjectNamesFromList(statementStructure, tokens, 1, visitedAliases, objectNames, "ONLY");
}
return;
}
if ("GRANT".equals(statementType) || "REVOKE".equals(statementType)) {
- collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, result, "TABLE", "VIEW", "INDEX", "SEQUENCE", "DATABASE", "SCHEMA");
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, objectNames, "TABLE", "VIEW", "INDEX", "SEQUENCE", "DATABASE", "SCHEMA");
}
}
private void collectDeleteTargetObjectNames(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> result) {
+ final Collection<String> objectNames) {
int deleteTargetStartIndex = skipDeleteModifierKeywords(tokens, 1);
if (deleteTargetStartIndex < tokens.size() && !scanner.isKeyword(tokens.get(deleteTargetStartIndex), "FROM", "USING")) {
- collectObjectNamesFromList(statementStructure, tokens, deleteTargetStartIndex, visitedAliases, result);
+ collectObjectNamesFromList(statementStructure, tokens, deleteTargetStartIndex, visitedAliases, objectNames);
}
- collectObjectNamesAfterKeyword(statementStructure, tokens, "FROM", visitedAliases, result, "ONLY");
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "FROM", visitedAliases, objectNames, "ONLY");
}
private int skipDeleteModifierKeywords(final List<SQLStatementToken> tokens, final int startIndex) {
@@ -197,37 +197,37 @@ final class SQLStatementTargetResolver {
}
private void collectCreateSourceObjectNames(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> result) {
+ final Collection<String> objectNames) {
if (isStatementObjectType(tokens, "INDEX")) {
- collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, result);
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, objectNames);
}
if (isStatementObjectType(tokens, "TRIGGER", "POLICY")) {
- collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, result);
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, objectNames);
}
if (isStatementObjectType(tokens, "TABLE")) {
- collectObjectNamesAfterKeyword(statementStructure, tokens, "LIKE", visitedAliases, result);
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "LIKE", visitedAliases, objectNames);
}
}
private void collectDropSourceObjectNames(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> result) {
+ final Collection<String> objectNames) {
if (isStatementObjectType(tokens, "INDEX")) {
- collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, result);
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, objectNames);
}
}
private void collectAlterDestinationObjectNames(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> result) {
+ final Collection<String> objectNames) {
for (int index = 0; index < tokens.size(); index++) {
if (isRenameToDestination(tokens, index)) {
- collectObjectNamesFromList(statementStructure, tokens, index + 2, visitedAliases, result);
+ collectObjectNamesFromList(statementStructure, tokens, index + 2, visitedAliases, objectNames);
}
if (isSetSchemaDestination(tokens, index)) {
- collectObjectNamesFromList(statementStructure, tokens, index + 2, visitedAliases, result);
+ collectObjectNamesFromList(statementStructure, tokens, index + 2, visitedAliases, objectNames);
}
}
if (isStatementObjectType(tokens, "TRIGGER", "POLICY")) {
- collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, result);
+ collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, objectNames);
}
}
@@ -265,20 +265,20 @@ final class SQLStatementTargetResolver {
}
private void collectObjectNamesAfterTypeKeyword(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> result, final String... optionalKeywords) {
+ final Collection<String> objectNames, final String... optionalKeywords) {
for (int index = 0; index < tokens.size(); index++) {
if (SQL_OBJECT_TYPE_KEYWORDS.contains(tokens.get(index).upperText())) {
- collectObjectNamesFromList(statementStructure, tokens, index + 1, visitedAliases, result, optionalKeywords);
+ collectObjectNamesFromList(statementStructure, tokens, index + 1, visitedAliases, objectNames, optionalKeywords);
return;
}
}
}
private boolean collectObjectNamesAfterKeyword(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final String keyword,
- final Collection<String> visitedAliases, final Collection<String> result, final String... optionalKeywords) {
+ final Collection<String> visitedAliases, final Collection<String> objectNames, final String... optionalKeywords) {
for (int index = 0; index < tokens.size(); index++) {
if (scanner.isKeyword(tokens.get(index), keyword)) {
- collectObjectNamesFromList(statementStructure, tokens, index + 1, visitedAliases, result, optionalKeywords);
+ collectObjectNamesFromList(statementStructure, tokens, index + 1, visitedAliases, objectNames, optionalKeywords);
return true;
}
}
@@ -286,14 +286,14 @@ final class SQLStatementTargetResolver {
}
private void collectObjectNamesFromList(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final int startIndex,
- final Collection<String> visitedAliases, final Collection<String> result, final String... optionalKeywords) {
+ final Collection<String> visitedAliases, final Collection<String> objectNames, final String... optionalKeywords) {
int currentIndex = startIndex;
while (currentIndex < tokens.size()) {
SQLStatementObjectName objectName = objectNameReader.readObjectName(tokens, currentIndex, optionalKeywords);
if (objectName.objectName().isEmpty()) {
return;
}
- collectObjectName(statementStructure, objectName.objectName(), visitedAliases, result);
+ collectObjectName(statementStructure, objectName.objectName(), visitedAliases, objectNames);
currentIndex = objectNameReader.skipObjectTail(tokens, objectName.nextIndex());
if (currentIndex >= tokens.size() || !",".equals(tokens.get(currentIndex).text())) {
return;
@@ -302,12 +302,12 @@ final class SQLStatementTargetResolver {
}
}
- private void collectQualifiedFunctionNames(final List<SQLStatementToken> tokens, final Collection<String> result) {
+ private void collectQualifiedFunctionNames(final List<SQLStatementToken> tokens, final Collection<String> objectNames) {
int index = 0;
while (index < tokens.size()) {
SQLStatementObjectName objectName = objectNameReader.readQualifiedName(tokens, index);
if (isQualifiedFunctionName(tokens, objectName)) {
- addObjectName(result, objectName.objectName());
+ addObjectName(objectNames, objectName.objectName());
index = objectName.nextIndex() - 1;
}
index++;
@@ -319,9 +319,9 @@ final class SQLStatementTargetResolver {
&& "(".equals(tokens.get(objectName.nextIndex()).text());
}
- private void addObjectName(final Collection<String> result, final String objectName) {
+ private void addObjectName(final Collection<String> objectNames, final String objectName) {
if (!objectName.isEmpty()) {
- result.add(objectName);
+ objectNames.add(objectName);
}
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/MetadataSearchCollector.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/MetadataSearchCollector.java
index e866eccb57e..4d3415b51ff 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/MetadataSearchCollector.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/MetadataSearchCollector.java
@@ -84,13 +84,13 @@ final class MetadataSearchCollector {
return result;
}
- private void addSchemaSearchHits(final List<MetadataSearchHit> result, final String databaseName, final String schemaName) {
+ private void addSchemaSearchHits(final List<MetadataSearchHit> searchHits, final String databaseName, final String schemaName) {
if (!schemaName.isEmpty()) {
- metadataQueryFacade.querySchema(databaseName, schemaName).ifPresent(optional -> result.add(createSearchHit(optional)));
+ metadataQueryFacade.querySchema(databaseName, schemaName).ifPresent(optional -> searchHits.add(createSearchHit(optional)));
return;
}
for (MCPSchemaMetadata each : metadataQueryFacade.querySchemas(databaseName)) {
- result.add(createSearchHit(each));
+ searchHits.add(createSearchHit(each));
}
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandler.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandler.java
index a037a0d0fe2..8f3f540b0c0 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandler.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandler.java
@@ -172,9 +172,9 @@ public final class SearchMetadataToolHandler implements MCPToolHandler<MCPDataba
}
}
- private void addDimensionIfAmbiguous(final List<String> result, final String dimension, final Set<String> values) {
+ private void addDimensionIfAmbiguous(final List<String> dimensions, final String dimension, final Set<String> values) {
if (1 < values.size()) {
- result.add(dimension);
+ dimensions.add(dimension);
}
}
@@ -189,7 +189,7 @@ public final class SearchMetadataToolHandler implements MCPToolHandler<MCPDataba
}
private Map<String, Object> createEmptyState(final MetadataSearchRequest request) {
- final Map<String, Object> result = new LinkedHashMap<>(3, 1F);
+ Map<String, Object> result = new LinkedHashMap<>(3, 1F);
result.put("state", request.getQuery().isEmpty() ? "no_items" : "no_match");
result.put("category", request.getQuery().isEmpty() ? "empty_scope" : "not_found");
result.put("reason", request.getQuery().isEmpty() ? "No metadata is available in the requested scope." : "No metadata matched the query in the requested scope.");
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowRuntimeDefinitionRegistry.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowRuntimeDefinitionRegistry.java
index 960b6462372..327168cdbe2 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowRuntimeDefinitionRegistry.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowRuntimeDefinitionRegistry.java
@@ -44,16 +44,16 @@ public final class WorkflowRuntimeDefinitionRegistry {
}
private static Map<WorkflowKind, WorkflowRuntimeDefinition> createRegisteredDefinitions(final Collection<WorkflowRuntimeDefinition> definitions) {
- final Map<WorkflowKind, WorkflowRuntimeDefinition> result = new LinkedHashMap<>(Math.max(definitions.size(), 1), 1F);
+ Map<WorkflowKind, WorkflowRuntimeDefinition> result = new LinkedHashMap<>(Math.max(definitions.size(), 1), 1F);
for (WorkflowRuntimeDefinition each : definitions) {
Objects.requireNonNull(each, "Workflow definition is required.");
- final WorkflowKind workflowKind = Objects.requireNonNull(each.getWorkflowKind(),
+ WorkflowKind workflowKind = Objects.requireNonNull(each.getWorkflowKind(),
() -> String.format("Workflow kind is required for `%s`.", each.getClass().getName()));
ShardingSpherePreconditions.checkNotNull(each.getValidationHandler(),
() -> new IllegalArgumentException(String.format("Workflow validation handler is required for `%s`.", workflowKind.getValue())));
ShardingSpherePreconditions.checkNotNull(each.getApplySynchronizationHandler(),
() -> new IllegalArgumentException(String.format("Workflow apply synchronization handler is required for `%s`.", workflowKind.getValue())));
- final WorkflowRuntimeDefinition previousDefinition = result.putIfAbsent(workflowKind, each);
+ WorkflowRuntimeDefinition previousDefinition = result.putIfAbsent(workflowKind, each);
ShardingSpherePreconditions.checkState(null == previousDefinition,
() -> new IllegalArgumentException(String.format("Duplicate workflow kind `%s` with `%s` and `%s`.",
workflowKind.getValue(), previousDefinition.getClass().getName(), each.getClass().getName())));
@@ -71,7 +71,7 @@ public final class WorkflowRuntimeDefinitionRegistry {
}
private static Collection<WorkflowRuntimeDefinition> loadDefinitions() {
- final Collection<WorkflowRuntimeDefinition> result = new LinkedList<>();
+ Collection<WorkflowRuntimeDefinition> result = new LinkedList<>();
for (MCPHandlerProvider each : ShardingSphereServiceLoader.getServiceInstances(MCPHandlerProvider.class)) {
if (each instanceof MCPWorkflowDefinitionProvider) {
result.addAll(createDefinitions((MCPWorkflowDefinitionProvider) each));
@@ -81,7 +81,7 @@ public final class WorkflowRuntimeDefinitionRegistry {
}
private static Collection<WorkflowRuntimeDefinition> createDefinitions(final MCPWorkflowDefinitionProvider workflowDefinitionProvider) {
- final Collection<WorkflowRuntimeDefinition> result = Objects.requireNonNull(workflowDefinitionProvider.getWorkflowDefinitions(),
+ Collection<WorkflowRuntimeDefinition> result = Objects.requireNonNull(workflowDefinitionProvider.getWorkflowDefinitions(),
() -> String.format("Workflow definitions are required for `%s`.", workflowDefinitionProvider.getClass().getName()));
result.forEach(each -> Objects.requireNonNull(each, () -> String.format("Workflow definition is required for `%s`.", workflowDefinitionProvider.getClass().getName())));
return result;
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/CoreResourceHandlerSurfaceTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/CoreResourceHandlerSurfaceTest.java
index f86d7f75871..fc4dbc2872a 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/CoreResourceHandlerSurfaceTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/CoreResourceHandlerSurfaceTest.java
@@ -51,7 +51,6 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -229,7 +228,8 @@ class CoreResourceHandlerSurfaceTest {
(requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryDatabases()), "shardingsphere://databases",
"shardingsphere://databases", HandlerResultType.METADATA, "", List.of("logic_db", "runtime_db", "warehouse")),
new HandlerCase("database", new MetadataResourceHandler("shardingsphere://databases/{database}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryDatabase(uriVariables.getValue("database")))),
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryDatabase(uriVariables.getValue("database"))
+ .map(CoreResourceHandlerSurfaceTest::createSingletonList).orElse(Collections.emptyList())),
"shardingsphere://databases/{database}",
"shardingsphere://databases/logic_db", HandlerResultType.METADATA, "", List.of("logic_db")),
new HandlerCase("database capabilities", new DatabaseCapabilitiesHandler(), "shardingsphere://databases/{database}/capabilities",
@@ -244,13 +244,15 @@ class CoreResourceHandlerSurfaceTest {
"shardingsphere://databases/{database}/schemas/{schema}/sequences",
"shardingsphere://databases/runtime_db/schemas/public/sequences", HandlerResultType.METADATA, "", List.of("order_seq")),
new HandlerCase("database schema sequence", new MetadataResourceHandler("shardingsphere://databases/{database}/schemas/{schema}/sequences/{sequence}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().querySequence(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("sequence")))),
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().querySequence(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("sequence"))
+ .map(CoreResourceHandlerSurfaceTest::createSingletonList).orElse(Collections.emptyList())),
"shardingsphere://databases/{database}/schemas/{schema}/sequences/{sequence}",
"shardingsphere://databases/runtime_db/schemas/public/sequences/order_seq", HandlerResultType.METADATA, "", List.of("order_seq")),
new HandlerCase("database schema", new MetadataResourceHandler("shardingsphere://databases/{database}/schemas/{schema}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().querySchema(
- uriVariables.getValue("database"), uriVariables.getValue("schema")))),
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().querySchema(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"))
+ .map(CoreResourceHandlerSurfaceTest::createSingletonList).orElse(Collections.emptyList())),
"shardingsphere://databases/{database}/schemas/{schema}",
"shardingsphere://databases/logic_db/schemas/public", HandlerResultType.METADATA, "", List.of("public")),
new HandlerCase("database schema tables", new MetadataResourceHandler("shardingsphere://databases/{database}/schemas/{schema}/tables",
@@ -264,8 +266,9 @@ class CoreResourceHandlerSurfaceTest {
"shardingsphere://databases/{database}/schemas/{schema}/views",
"shardingsphere://databases/logic_db/schemas/public/views", HandlerResultType.METADATA, "", List.of("orders_view")),
new HandlerCase("database schema table", new MetadataResourceHandler("shardingsphere://databases/{database}/schemas/{schema}/tables/{table}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryTable(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table")))),
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryTable(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"))
+ .map(CoreResourceHandlerSurfaceTest::createSingletonList).orElse(Collections.emptyList())),
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}",
"shardingsphere://databases/logic_db/schemas/public/tables/orders", HandlerResultType.METADATA, "", List.of("orders")),
new HandlerCase("database schema table columns", new MetadataResourceHandler(
@@ -276,13 +279,15 @@ class CoreResourceHandlerSurfaceTest {
"shardingsphere://databases/logic_db/schemas/public/tables/orders/columns", HandlerResultType.METADATA, "", List.of("order_id")),
new HandlerCase("database schema table column", new MetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/columns/{column}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryTableColumn(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"), uriVariables.getValue("column")))),
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryTableColumn(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"), uriVariables.getValue("column"))
+ .map(CoreResourceHandlerSurfaceTest::createSingletonList).orElse(Collections.emptyList())),
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/columns/{column}",
"shardingsphere://databases/logic_db/schemas/public/tables/orders/columns/order_id", HandlerResultType.METADATA, "", List.of("order_id")),
new HandlerCase("database schema view", new MetadataResourceHandler("shardingsphere://databases/{database}/schemas/{schema}/views/{view}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryView(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view")))),
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryView(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view"))
+ .map(CoreResourceHandlerSurfaceTest::createSingletonList).orElse(Collections.emptyList())),
"shardingsphere://databases/{database}/schemas/{schema}/views/{view}",
"shardingsphere://databases/logic_db/schemas/public/views/orders_view", HandlerResultType.METADATA, "", List.of("orders_view")),
new HandlerCase("database schema view columns", new MetadataResourceHandler(
@@ -293,8 +298,9 @@ class CoreResourceHandlerSurfaceTest {
"shardingsphere://databases/logic_db/schemas/public/views/orders_view/columns", HandlerResultType.METADATA, "", List.of("order_id")),
new HandlerCase("database schema view column", new MetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/views/{view}/columns/{column}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryViewColumn(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view"), uriVariables.getValue("column")))),
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryViewColumn(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("view"), uriVariables.getValue("column"))
+ .map(CoreResourceHandlerSurfaceTest::createSingletonList).orElse(Collections.emptyList())),
"shardingsphere://databases/{database}/schemas/{schema}/views/{view}/columns/{column}",
"shardingsphere://databases/logic_db/schemas/public/views/orders_view/columns/order_id", HandlerResultType.METADATA, "", List.of("order_id")),
new HandlerCase("database schema table indexes", new MetadataResourceHandler(
@@ -305,14 +311,15 @@ class CoreResourceHandlerSurfaceTest {
"shardingsphere://databases/logic_db/schemas/public/tables/orders/indexes", HandlerResultType.METADATA, "", List.of("order_idx")),
new HandlerCase("database schema table index", new MetadataResourceHandler(
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/indexes/{index}",
- (requestContext, uriVariables) -> singletonOrEmpty(requestContext.getMetadataQueryFacade().queryIndex(
- uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"), uriVariables.getValue("index")))),
+ (requestContext, uriVariables) -> requestContext.getMetadataQueryFacade().queryIndex(
+ uriVariables.getValue("database"), uriVariables.getValue("schema"), uriVariables.getValue("table"), uriVariables.getValue("index"))
+ .map(CoreResourceHandlerSurfaceTest::createSingletonList).orElse(Collections.emptyList())),
"shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/indexes/{index}",
"shardingsphere://databases/logic_db/schemas/public/tables/orders/indexes/order_idx", HandlerResultType.METADATA, "", List.of("order_idx")));
}
- private static List<?> singletonOrEmpty(final Optional<?> metadata) {
- return metadata.map(Collections::singletonList).orElse(Collections.emptyList());
+ private static List<?> createSingletonList(final Object metadata) {
+ return Collections.singletonList(metadata);
}
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacadeTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacadeTest.java
index 721d9efec94..82ae1a8a383 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacadeTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacadeTest.java
@@ -62,7 +62,7 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory);
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory);
final SQLExecutionRequest request = createExecutionRequest("SELECT 1");
when(coordinator.executeWithSessionLock(eq("session-1"), any())).thenThrow(new MCPSessionNotExistedException());
MCPSessionNotExistedException actual = assertThrows(MCPSessionNotExistedException.class, () -> facade.execute(request));
@@ -78,8 +78,8 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory);
- final SQLExecutionRequest request = createExecutionRequest("SELECT 1");
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory);
+ SQLExecutionRequest request = createExecutionRequest("SELECT 1");
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.empty());
DatabaseCapabilityNotFoundException actual = assertThrows(DatabaseCapabilityNotFoundException.class, () -> facade.execute(request));
@@ -92,16 +92,16 @@ class MCPSQLExecutionFacadeTest {
void assertExecuteWithUnsupportedStatement() {
MCPDatabaseCapabilityProvider capabilityProvider = mock(MCPDatabaseCapabilityProvider.class);
MCPSessionExecutionCoordinator coordinator = mock(MCPSessionExecutionCoordinator.class);
- MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
- MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
- SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
- final SQLExecutionRequest request = createExecutionRequest("SELECT 1");
StatementClassifier statementClassifier = mock(StatementClassifier.class);
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, statementClassifier);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY), false);
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
when(statementClassifier.classify(anyString())).thenThrow(new MCPBannedSQLStatementException());
+ MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
+ MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
+ SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, statementClassifier);
+ SQLExecutionRequest request = createExecutionRequest("SELECT 1");
MCPBannedSQLStatementException actual = assertThrows(MCPBannedSQLStatementException.class, () -> facade.execute(request));
assertThat(actual.getMessage(), is("Statement is banned by the MCP contract."));
verify(traceFactory).create("session-1", "logic_db", "SELECT 1", false, "QUERY");
@@ -115,9 +115,9 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
- final SQLExecutionRequest request = createExecutionRequest("SELECT 1");
+ SQLExecutionRequest request = createExecutionRequest("SELECT 1");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT 1", "orders", "");
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.DML), false);
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
@@ -157,7 +157,7 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY), false);
- final SQLExecutionRequest request = createExecutionRequest("SELECT 1");
+ SQLExecutionRequest request = createExecutionRequest("SELECT 1");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT 1", "orders", "");
SQLExecutionResponse response = SQLExecutionResponse.resultSet(SupportedMCPStatement.QUERY, "SELECT", Collections.emptyList(), Collections.emptyList(), false);
final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
@@ -179,7 +179,7 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY), false);
- final SQLExecutionRequest request = createExecutionRequest("SELECT * FROM other_db.orders");
+ SQLExecutionRequest request = createExecutionRequest("SELECT * FROM other_db.orders");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM other_db.orders", "other_db.orders", "", null,
List.of("other_db.orders"));
final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
@@ -199,8 +199,8 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY), false);
- final SQLExecutionRequest request = createExecutionRequest("SELECT * FROM logic_db.orders WHERE EXISTS (SELECT 1 FROM other_db.items)");
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory);
+ SQLExecutionRequest request = createExecutionRequest("SELECT * FROM logic_db.orders WHERE EXISTS (SELECT 1 FROM other_db.items)");
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory);
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
MCPInvalidRequestException actual = assertThrows(MCPInvalidRequestException.class, () -> facade.execute(request));
@@ -218,8 +218,8 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY, SupportedMCPStatement.DML, SupportedMCPStatement.DDL, SupportedMCPStatement.DCL), false);
- final SQLExecutionRequest request = createExecutionRequest(sql);
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory);
+ SQLExecutionRequest request = createExecutionRequest(sql);
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory);
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
MCPInvalidRequestException actual = assertThrows(MCPInvalidRequestException.class, () -> facade.execute(request));
@@ -236,7 +236,7 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.DML), false);
- final SQLExecutionRequest request = createExecutionRequest("UPDATE other_db.orders SET status = 'DONE'");
+ SQLExecutionRequest request = createExecutionRequest("UPDATE other_db.orders SET status = 'DONE'");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.DML, "UPDATE", "UPDATE other_db.orders SET status = 'DONE'", "other_db.orders", "",
null, List.of("other_db.orders"));
final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
@@ -256,7 +256,7 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY), false);
- final SQLExecutionRequest request = createExecutionRequest("SELECT * FROM logic_db.orders");
+ SQLExecutionRequest request = createExecutionRequest("SELECT * FROM logic_db.orders");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM logic_db.orders", "logic_db.orders", "", null,
List.of("logic_db.orders"));
SQLExecutionResponse response = SQLExecutionResponse.resultSet(SupportedMCPStatement.QUERY, "SELECT", Collections.emptyList(), Collections.emptyList(), false);
@@ -279,7 +279,7 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY), false, true);
- final SQLExecutionRequest request = createExecutionRequest("SELECT * FROM other_db.orders");
+ SQLExecutionRequest request = createExecutionRequest("SELECT * FROM other_db.orders");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM other_db.orders", "other_db.orders", "", null,
List.of("other_db.orders"));
SQLExecutionResponse response = SQLExecutionResponse.resultSet(SupportedMCPStatement.QUERY, "SELECT", Collections.emptyList(), Collections.emptyList(), false);
@@ -301,10 +301,10 @@ class MCPSQLExecutionFacadeTest {
MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
- final SQLExecutionRequest request = createExecutionRequest("EXPLAIN ANALYZE SELECT 1");
+ SQLExecutionRequest request = createExecutionRequest("EXPLAIN ANALYZE SELECT 1");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.EXPLAIN_ANALYZE, "EXPLAIN ANALYZE", "EXPLAIN ANALYZE SELECT 1", "orders", "");
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.EXPLAIN_ANALYZE), false);
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
MCPUnsupportedException actual = assertThrows(MCPUnsupportedException.class, () -> facade.execute(request));
@@ -317,16 +317,16 @@ class MCPSQLExecutionFacadeTest {
void assertExecuteWithExecutorFailure() {
MCPDatabaseCapabilityProvider capabilityProvider = mock(MCPDatabaseCapabilityProvider.class);
MCPSessionExecutionCoordinator coordinator = mock(MCPSessionExecutionCoordinator.class);
- MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
- MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
- SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY), false);
- final SQLExecutionRequest request = createExecutionRequest("SELECT 1");
+ SQLExecutionRequest request = createExecutionRequest("SELECT 1");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT 1", "orders", "");
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
+ MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
when(statementExecutor.execute(request, classification, capability)).thenThrow(new MCPInvalidRequestException("bad query"));
+ MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
+ SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
MCPInvalidRequestException actual = assertThrows(MCPInvalidRequestException.class, () -> facade.execute(request));
assertThat(actual.getMessage(), is("bad query"));
verify(traceFactory).create("session-1", "logic_db", "SELECT 1", false, "QUERY");
@@ -337,17 +337,17 @@ class MCPSQLExecutionFacadeTest {
void assertExecuteExplainAnalyze() {
MCPDatabaseCapabilityProvider capabilityProvider = mock(MCPDatabaseCapabilityProvider.class);
MCPSessionExecutionCoordinator coordinator = mock(MCPSessionExecutionCoordinator.class);
- MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
- MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
- SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.EXPLAIN_ANALYZE), true);
- final SQLExecutionRequest request = createExecutionRequest("EXPLAIN ANALYZE SELECT 1");
+ SQLExecutionRequest request = createExecutionRequest("EXPLAIN ANALYZE SELECT 1");
ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.EXPLAIN_ANALYZE, "EXPLAIN ANALYZE", "EXPLAIN ANALYZE SELECT 1", "orders", "");
SQLExecutionResponse response = SQLExecutionResponse.resultSet(SupportedMCPStatement.EXPLAIN_ANALYZE, "EXPLAIN ANALYZE", Collections.emptyList(), Collections.emptyList(), false);
- final MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
+ MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
when(statementExecutor.execute(request, classification, capability)).thenReturn(response);
+ MCPJdbcTransactionStatementExecutor transactionExecutor = mock(MCPJdbcTransactionStatementExecutor.class);
+ SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
SQLExecutionResponse actual = facade.execute(request);
assertThat(actual, is(response));
verify(statementExecutor).execute(request, classification, capability);
diff --git a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/DerivedColumnNamingService.java b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/DerivedColumnNamingService.java
index 29486102a77..48dfa964bb7 100644
--- a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/DerivedColumnNamingService.java
+++ b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/DerivedColumnNamingService.java
@@ -24,6 +24,7 @@ import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowIssueCode;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowSQLUtils;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -40,7 +41,7 @@ public final class DerivedColumnNamingService {
* @param issues workflow issues
* @return derived column plan
*/
- public DerivedColumnPlan createPlan(final EncryptWorkflowRequest request, final Set<String> existingNames, final java.util.List<WorkflowIssue> issues) {
+ public DerivedColumnPlan createPlan(final EncryptWorkflowRequest request, final Set<String> existingNames, final List<WorkflowIssue> issues) {
boolean requiresAssistedQuery = Boolean.TRUE.equals(request.getOptions().getRequiresEqualityFilter());
boolean requiresLikeQuery = Boolean.TRUE.equals(request.getOptions().getRequiresLikeQuery());
DerivedColumnPlan result = new DerivedColumnPlan();
@@ -60,7 +61,7 @@ public final class DerivedColumnNamingService {
}
private String resolveName(final String defaultName, final String overrideName, final Set<String> existingNames,
- final java.util.List<WorkflowIssue> issues, final DerivedColumnPlan plan) {
+ final List<WorkflowIssue> issues, final DerivedColumnPlan plan) {
String candidate = overrideName.isEmpty() ? defaultName : overrideName;
if (!WorkflowSQLUtils.isSafeIdentifier(candidate)) {
issues.add(new WorkflowIssue(WorkflowIssueCode.USER_OVERRIDE_NAME_UNSAFE, "error", "planning-artifacts",
diff --git a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptAlgorithmRecommendationService.java b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptAlgorithmRecommendationService.java
index 8e9cb936876..5125bc3acb1 100644
--- a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptAlgorithmRecommendationService.java
+++ b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptAlgorithmRecommendationService.java
@@ -96,7 +96,7 @@ public final class EncryptAlgorithmRecommendationService {
return encryptAlgorithms.isEmpty() ? "" : getAlgorithmType(encryptAlgorithms.get(0));
}
- private void addAssistedQueryCandidate(final List<AlgorithmCandidate> result, final EncryptWorkflowRequest request,
+ private void addAssistedQueryCandidate(final List<AlgorithmCandidate> candidates, final EncryptWorkflowRequest request,
final List<Map<String, Object>> encryptAlgorithms, final List<WorkflowIssue> issues) {
String assistedQueryType = resolveAssistedQueryAlgorithm(request, encryptAlgorithms, issues);
if (assistedQueryType.isEmpty()) {
@@ -105,10 +105,10 @@ public final class EncryptAlgorithmRecommendationService {
return;
}
AlgorithmCandidate assistedQueryCandidate = createEncryptCandidate("assisted_query", assistedQueryType, request);
- result.add(assistedQueryCandidate);
+ candidates.add(assistedQueryCandidate);
}
- private void addLikeQueryCandidate(final List<AlgorithmCandidate> result, final EncryptWorkflowRequest request,
+ private void addLikeQueryCandidate(final List<AlgorithmCandidate> candidates, final EncryptWorkflowRequest request,
final List<Map<String, Object>> encryptAlgorithms, final List<WorkflowIssue> issues) {
String likeQueryType = resolveLikeQueryAlgorithm(request, encryptAlgorithms, issues);
if (likeQueryType.isEmpty()) {
@@ -117,7 +117,7 @@ public final class EncryptAlgorithmRecommendationService {
return;
}
AlgorithmCandidate likeQueryCandidate = createEncryptCandidate("like_query", likeQueryType, request);
- result.add(likeQueryCandidate);
+ candidates.add(likeQueryCandidate);
}
private void addCapabilityConflictIssue(final List<WorkflowIssue> issues, final String specifiedAlgorithmType, final String message, final String userAction) {
diff --git a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowValidationService.java b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowValidationService.java
index d8dcc3df2f9..74b7367e948 100644
--- a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowValidationService.java
+++ b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowValidationService.java
@@ -99,7 +99,7 @@ public final class EncryptWorkflowValidationService implements MCPWorkflowRuntim
if (snapshot.getRequest() instanceof EncryptWorkflowRequest) {
return (EncryptWorkflowRequest) snapshot.getRequest();
}
- final EncryptWorkflowRequest result = EncryptWorkflowRequest.merge(snapshot.getRequest(), null);
+ EncryptWorkflowRequest result = EncryptWorkflowRequest.merge(snapshot.getRequest(), null);
return null == result ? new EncryptWorkflowRequest() : result;
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoader.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoader.java
index 2c3b72cf95f..1614e930477 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoader.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoader.java
@@ -101,71 +101,71 @@ public final class MCPJdbcDatabaseProfileLoader {
private String determineActualDatabaseType(final String databaseName, final DatabaseMetaData databaseMetaData) throws SQLException {
String productName = Objects.toString(databaseMetaData.getDatabaseProductName(), "").trim();
String jdbcUrl = Objects.toString(databaseMetaData.getURL(), "").trim();
- String result = resolveDatabaseTypeFromProductName(productName, jdbcUrl);
- if (null == result) {
+ Optional<String> result = resolveDatabaseTypeFromProductName(productName, jdbcUrl);
+ if (result.isEmpty()) {
result = resolveDatabaseTypeFromJdbcUrl(jdbcUrl);
}
- if (null == result) {
+ if (result.isEmpty()) {
throw RuntimeDatabaseConnectionException.invalidConfiguration(databaseName,
new IllegalStateException(String.format("Actual database type cannot be determined for database `%s`.", databaseName)));
}
- return result;
+ return result.get();
}
- private String resolveDatabaseTypeFromProductName(final String productName, final String jdbcUrl) {
+ private Optional<String> resolveDatabaseTypeFromProductName(final String productName, final String jdbcUrl) {
if (!productName.isEmpty()) {
if (productName.toUpperCase(Locale.ENGLISH).contains("POSTGRESQL")) {
- return jdbcUrl.toLowerCase(Locale.ENGLISH).startsWith("jdbc:opengauss:") ? "openGauss" : "PostgreSQL";
+ return Optional.of(jdbcUrl.toLowerCase(Locale.ENGLISH).startsWith("jdbc:opengauss:") ? "openGauss" : "PostgreSQL");
}
if (productName.toUpperCase(Locale.ENGLISH).contains("SQL SERVER")) {
- return "SQLServer";
+ return Optional.of("SQLServer");
}
if (productName.toUpperCase(Locale.ENGLISH).contains("MARIADB")) {
- return "MariaDB";
+ return Optional.of("MariaDB");
}
if (productName.toUpperCase(Locale.ENGLISH).contains("MYSQL")) {
- return "MySQL";
+ return Optional.of("MySQL");
}
if (productName.toUpperCase(Locale.ENGLISH).contains("ORACLE")) {
- return "Oracle";
+ return Optional.of("Oracle");
}
if (productName.toUpperCase(Locale.ENGLISH).contains("FIREBIRD")) {
- return "Firebird";
+ return Optional.of("Firebird");
}
if (productName.toUpperCase(Locale.ENGLISH).contains("H2")) {
- return "H2";
+ return Optional.of("H2");
}
}
- return TypedSPILoader.findService(MCPDatabaseCapabilityOption.class, productName).map(MCPDatabaseCapabilityOption::getType).orElse(null);
+ return TypedSPILoader.findService(MCPDatabaseCapabilityOption.class, productName).map(MCPDatabaseCapabilityOption::getType);
}
- private String resolveDatabaseTypeFromJdbcUrl(final String jdbcUrl) {
+ private Optional<String> resolveDatabaseTypeFromJdbcUrl(final String jdbcUrl) {
String actualJdbcUrl = jdbcUrl.toLowerCase(Locale.ENGLISH);
if (actualJdbcUrl.startsWith("jdbc:opengauss:")) {
- return "openGauss";
+ return Optional.of("openGauss");
}
if (actualJdbcUrl.startsWith("jdbc:postgresql:")) {
- return "PostgreSQL";
+ return Optional.of("PostgreSQL");
}
if (actualJdbcUrl.startsWith("jdbc:sqlserver:")) {
- return "SQLServer";
+ return Optional.of("SQLServer");
}
if (actualJdbcUrl.startsWith("jdbc:mariadb:")) {
- return "MariaDB";
+ return Optional.of("MariaDB");
}
if (actualJdbcUrl.startsWith("jdbc:mysql:")) {
- return "MySQL";
+ return Optional.of("MySQL");
}
if (actualJdbcUrl.startsWith("jdbc:oracle:")) {
- return "Oracle";
+ return Optional.of("Oracle");
}
if (actualJdbcUrl.startsWith("jdbc:firebirdsql:")) {
- return "Firebird";
+ return Optional.of("Firebird");
}
if (actualJdbcUrl.startsWith("jdbc:h2:")) {
- return "H2";
+ return Optional.of("H2");
}
- return null;
+ return Optional.empty();
}
private Optional<String> resolveCompatibleBranchDatabaseType(final Connection connection, final String configuredDatabaseType,
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoader.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoader.java
index 54068e15f7c..7ab3a8b54e5 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoader.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoader.java
@@ -42,6 +42,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
/**
@@ -141,11 +142,11 @@ public final class MCPJdbcMetadataLoader {
private void loadSequences(final String databaseName, final SchemaSemantics defaultSchemaSemantics, final String databaseType,
final DatabaseMetadataAccumulator accumulator, final Connection connection) throws SQLException {
- String sequenceQuery = getSequenceQuery(databaseType);
- if (null == sequenceQuery) {
+ Optional<String> sequenceQuery = getSequenceQuery(databaseType);
+ if (sequenceQuery.isEmpty()) {
return;
}
- try (Statement statement = connection.createStatement(); ResultSet sequences = statement.executeQuery(sequenceQuery)) {
+ try (Statement statement = connection.createStatement(); ResultSet sequences = statement.executeQuery(sequenceQuery.get())) {
while (sequences.next()) {
String schemaName = Objects.toString(sequences.getString("SEQUENCE_SCHEMA"), "").trim();
if (isSystemSchema(schemaName)) {
@@ -160,26 +161,26 @@ public final class MCPJdbcMetadataLoader {
}
}
- private String getSequenceQuery(final String databaseType) {
+ private Optional<String> getSequenceQuery(final String databaseType) {
if (null == databaseType || databaseType.isBlank()) {
- return null;
+ return Optional.empty();
}
switch (databaseType.toUpperCase(Locale.ENGLISH)) {
case "POSTGRESQL":
case "OPENGAUSS":
- return INFORMATION_SCHEMA_SEQUENCE_QUERY;
+ return Optional.of(INFORMATION_SCHEMA_SEQUENCE_QUERY);
case "SQLSERVER":
- return SQL_SERVER_SEQUENCE_QUERY;
+ return Optional.of(SQL_SERVER_SEQUENCE_QUERY);
case "ORACLE":
- return ORACLE_SEQUENCE_QUERY;
+ return Optional.of(ORACLE_SEQUENCE_QUERY);
case "MARIADB":
- return MARIADB_SEQUENCE_QUERY;
+ return Optional.of(MARIADB_SEQUENCE_QUERY);
case "FIREBIRD":
- return FIREBIRD_SEQUENCE_QUERY;
+ return Optional.of(FIREBIRD_SEQUENCE_QUERY);
case "H2":
- return "SELECT SEQUENCE_SCHEMA, SEQUENCE_NAME FROM INFORMATION_SCHEMA.SEQUENCES";
+ return Optional.of("SELECT SEQUENCE_SCHEMA, SEQUENCE_NAME FROM INFORMATION_SCHEMA.SEQUENCES");
default:
- return null;
+ return Optional.empty();
}
}
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 8b3cd1ca010..8cae0facf2e 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
@@ -75,7 +75,7 @@ final class MCPDescriptorCatalogValidator {
ShardingSpherePreconditions.checkState(each.isTemplated(),
() -> new IllegalStateException(String.format("Resource template `%s` must contain template variables.", each.getUriTemplate())));
validateResourceDescriptor(each, registered);
- validateResourceVariables(each, findShardingSphereResourceMetadata(catalog, each.getUriTemplate()));
+ validateResourceVariables(each, findShardingSphereResourceMetadata(catalog, each.getUriTemplate()).map(ShardingSphereMCPResourceMetadata::getUriVariables).orElse(List.of()));
}
}
@@ -84,11 +84,11 @@ final class MCPDescriptorCatalogValidator {
() -> new IllegalStateException(String.format("Duplicate MCP resource descriptor `%s`.", descriptor.getUriTemplate())));
}
- private static ShardingSphereMCPResourceMetadata findShardingSphereResourceMetadata(final MCPDescriptorCatalog catalog, final String uriOrTemplate) {
- return catalog.getShardingSphereResourceMetadata().stream().filter(each -> uriOrTemplate.equals(each.getUriOrTemplate())).findFirst().orElse(null);
+ private static Optional<ShardingSphereMCPResourceMetadata> findShardingSphereResourceMetadata(final MCPDescriptorCatalog catalog, final String uriOrTemplate) {
+ return catalog.getShardingSphereResourceMetadata().stream().filter(each -> uriOrTemplate.equals(each.getUriOrTemplate())).findFirst();
}
- private static void validateResourceVariables(final MCPResourceDescriptor descriptor, final ShardingSphereMCPResourceMetadata metadata) {
+ private static void validateResourceVariables(final MCPResourceDescriptor descriptor, final Collection<MCPUriVariableDescriptor> uriVariables) {
List<String> templateVariables = new MCPUriTemplate(descriptor.getUriTemplate()).getVariableNames();
Set<String> registeredTemplateVariables = new HashSet<>();
for (String each : templateVariables) {
@@ -96,7 +96,6 @@ final class MCPDescriptorCatalogValidator {
() -> new IllegalStateException(String.format("Duplicate URI template variable `%s` in resource descriptor `%s`.", each, descriptor.getUriTemplate())));
}
Set<String> templateVariableSet = new HashSet<>(templateVariables);
- Collection<MCPUriVariableDescriptor> uriVariables = null == metadata ? List.of() : metadata.getUriVariables();
Map<String, MCPUriVariableDescriptor> declaredParameters = new LinkedHashMap<>(uriVariables.size(), 1F);
for (MCPUriVariableDescriptor each : uriVariables) {
ShardingSpherePreconditions.checkState(each.isRequired(), () -> new IllegalStateException(
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPResourceHintUtils.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPResourceHintUtils.java
index fd8d3e0fd19..dde9a017c95 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPResourceHintUtils.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPResourceHintUtils.java
@@ -40,7 +40,7 @@ public final class MCPResourceHintUtils {
* @return typed resource hint
*/
public static Map<String, Object> create(final String uri, final String resourceKind, final String purpose, final String reason, final String sourceField) {
- final Map<String, Object> result = new LinkedHashMap<>(5, 1F);
+ Map<String, Object> result = new LinkedHashMap<>(5, 1F);
result.put("uri", uri);
result.put("resource_kind", resourceKind);
result.put("purpose", purpose);
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowGuidancePayloadBuilder.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowGuidancePayloadBuilder.java
index de043d39185..6a2b1589865 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowGuidancePayloadBuilder.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowGuidancePayloadBuilder.java
@@ -134,10 +134,10 @@ public final class WorkflowGuidancePayloadBuilder {
}
private static List<String> createMissingRequiredInputs(final WorkflowContextSnapshot snapshot) {
- final List<String> result = new LinkedList<>();
- final ClarifiedIntent clarifiedIntent = snapshot.getClarifiedIntent();
+ List<String> result = new LinkedList<>();
+ ClarifiedIntent clarifiedIntent = snapshot.getClarifiedIntent();
for (final String each : clarifiedIntent.getUnresolvedFields()) {
- final String missingInput = normalizeMissingInput(snapshot, each);
+ String missingInput = normalizeMissingInput(snapshot, each);
if (!result.contains(missingInput)) {
result.add(missingInput);
}
@@ -152,10 +152,10 @@ public final class WorkflowGuidancePayloadBuilder {
}
private static List<Map<String, Object>> createClarificationQuestions(final WorkflowContextSnapshot snapshot, final List<String> missingRequiredInputs) {
- final List<Map<String, Object>> result = new LinkedList<>();
- final List<String> clarificationMessages = snapshot.getClarifiedIntent().getClarificationMessages();
+ List<Map<String, Object>> result = new LinkedList<>();
+ List<String> clarificationMessages = snapshot.getClarifiedIntent().getClarificationMessages();
for (int i = 0; i < missingRequiredInputs.size(); i++) {
- final String fieldName = missingRequiredInputs.get(i);
+ String fieldName = missingRequiredInputs.get(i);
result.add(createClarificationQuestion(snapshot, fieldName, i < clarificationMessages.size() ? clarificationMessages.get(i) : ""));
}
return result;
@@ -195,16 +195,16 @@ public final class WorkflowGuidancePayloadBuilder {
return false;
}
- private static void addMissingInputsFromIssue(final Collection<String> result, final WorkflowContextSnapshot snapshot, final WorkflowIssue issue) {
- if (WorkflowIssueCode.DATABASE_REQUIRED.equals(issue.getCode()) && !result.contains(WorkflowFieldNames.DATABASE)) {
- result.add(WorkflowFieldNames.DATABASE);
+ private static void addMissingInputsFromIssue(final Collection<String> missingInputs, final WorkflowContextSnapshot snapshot, final WorkflowIssue issue) {
+ if (WorkflowIssueCode.DATABASE_REQUIRED.equals(issue.getCode()) && !missingInputs.contains(WorkflowFieldNames.DATABASE)) {
+ missingInputs.add(WorkflowFieldNames.DATABASE);
}
Object missingProperties = issue.getDetails().get("missing_properties");
if (missingProperties instanceof Collection) {
for (Object each : (Collection<?>) missingProperties) {
String missingInput = resolveAlgorithmPropertyInput(snapshot, String.valueOf(each));
- if (!result.contains(missingInput)) {
- result.add(missingInput);
+ if (!missingInputs.contains(missingInput)) {
+ missingInputs.add(missingInput);
}
}
}
@@ -249,9 +249,9 @@ public final class WorkflowGuidancePayloadBuilder {
}
private static List<Map<String, Object>> createResourcesToRead(final WorkflowContextSnapshot snapshot) {
- final List<Map<String, Object>> result = new LinkedList<>();
+ List<Map<String, Object>> result = new LinkedList<>();
addFeatureResources(result, snapshot);
- final WorkflowRequest request = snapshot.getRequest();
+ WorkflowRequest request = snapshot.getRequest();
if (!request.getDatabase().isEmpty()) {
addRuleResources(result, snapshot, request);
if (!request.getSchema().isEmpty() && !request.getTable().isEmpty()) {
@@ -261,36 +261,36 @@ public final class WorkflowGuidancePayloadBuilder {
return result;
}
- private static void addFeatureResources(final Collection<Map<String, Object>> result, final WorkflowContextSnapshot snapshot) {
+ private static void addFeatureResources(final Collection<Map<String, Object>> resourcesToRead, final WorkflowContextSnapshot snapshot) {
String workflowKind = resolveWorkflowKind(snapshot);
if ("encrypt.rule".equals(workflowKind)) {
- result.add(MCPResourceHintUtils.create("shardingsphere://features/encrypt/algorithms", "algorithm", "read_first",
+ resourcesToRead.add(MCPResourceHintUtils.create("shardingsphere://features/encrypt/algorithms", "algorithm", "read_first",
"Read encrypt algorithm metadata before choosing algorithm arguments.", "resources_to_read"));
} else if ("mask.rule".equals(workflowKind)) {
- result.add(MCPResourceHintUtils.create("shardingsphere://features/mask/algorithms", "algorithm", "read_first",
+ resourcesToRead.add(MCPResourceHintUtils.create("shardingsphere://features/mask/algorithms", "algorithm", "read_first",
"Read mask algorithm metadata before choosing algorithm arguments.", "resources_to_read"));
}
}
- private static void addRuleResources(final Collection<Map<String, Object>> result, final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
+ private static void addRuleResources(final Collection<Map<String, Object>> resourcesToRead, final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
String workflowKind = resolveWorkflowKind(snapshot);
if ("encrypt.rule".equals(workflowKind)) {
- result.add(MCPResourceHintUtils.create(String.format("shardingsphere://features/encrypt/databases/%s/rules",
+ resourcesToRead.add(MCPResourceHintUtils.create(String.format("shardingsphere://features/encrypt/databases/%s/rules",
MCPUriPathSegmentUtils.encodePathSegment(request.getDatabase())), "rule", "inspect_detail",
"Inspect current encrypt rules before planning changes.", "resources_to_read"));
} else if ("mask.rule".equals(workflowKind)) {
- result.add(MCPResourceHintUtils.create(String.format("shardingsphere://features/mask/databases/%s/rules",
+ resourcesToRead.add(MCPResourceHintUtils.create(String.format("shardingsphere://features/mask/databases/%s/rules",
MCPUriPathSegmentUtils.encodePathSegment(request.getDatabase())), "rule", "inspect_detail",
"Inspect current mask rules before planning changes.", "resources_to_read"));
}
}
- private static void addTableResources(final Collection<Map<String, Object>> result, final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
- result.add(MCPResourceHintUtils.create(String.format("shardingsphere://databases/%s/schemas/%s/tables/%s/columns", MCPUriPathSegmentUtils.encodePathSegment(request.getDatabase()),
+ private static void addTableResources(final Collection<Map<String, Object>> resourcesToRead, final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
+ resourcesToRead.add(MCPResourceHintUtils.create(String.format("shardingsphere://databases/%s/schemas/%s/tables/%s/columns", MCPUriPathSegmentUtils.encodePathSegment(request.getDatabase()),
MCPUriPathSegmentUtils.encodePathSegment(request.getSchema()), MCPUriPathSegmentUtils.encodePathSegment(request.getTable())),
"column", "validate_scope", "Read table columns before planning column-level workflow changes.", "resources_to_read"));
if ("encrypt.rule".equals(resolveWorkflowKind(snapshot))) {
- result.add(MCPResourceHintUtils.create(String.format("shardingsphere://databases/%s/schemas/%s/tables/%s/indexes", MCPUriPathSegmentUtils.encodePathSegment(request.getDatabase()),
+ resourcesToRead.add(MCPResourceHintUtils.create(String.format("shardingsphere://databases/%s/schemas/%s/tables/%s/indexes", MCPUriPathSegmentUtils.encodePathSegment(request.getDatabase()),
MCPUriPathSegmentUtils.encodePathSegment(request.getSchema()), MCPUriPathSegmentUtils.encodePathSegment(request.getTable())),
"index", "validate_scope", "Read table indexes before planning assisted-query encrypt rules.", "resources_to_read"));
}
@@ -330,7 +330,7 @@ public final class WorkflowGuidancePayloadBuilder {
}
private static List<Map<String, Object>> addSequencing(final List<Map<String, Object>> nextActions) {
- final List<Map<String, Object>> result = new LinkedList<>(nextActions);
+ List<Map<String, Object>> result = new LinkedList<>(nextActions);
for (int index = 0; index < result.size(); index++) {
result.get(index).put("order", index + 1);
if (0 < index && "ask_user".equals(result.get(index - 1).get("type"))) {
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowRequestBinder.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowRequestBinder.java
index 505600cac34..46b564bd767 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowRequestBinder.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowRequestBinder.java
@@ -123,14 +123,14 @@ public final class WorkflowRequestBinder {
}
}
- private static void addUserOverrideConflict(final Collection<String> conflicts, final Map<String, Object> arguments, final Entry<String, Object> entry) {
- if (!arguments.containsKey(entry.getKey())) {
+ private static void addUserOverrideConflict(final Collection<String> conflicts, final Map<String, Object> arguments, final Entry<String, Object> userOverride) {
+ if (!arguments.containsKey(userOverride.getKey())) {
return;
}
- String actualValue = normalizeComparableValue(arguments.get(entry.getKey()));
- String overrideValue = normalizeComparableValue(entry.getValue());
+ String actualValue = normalizeComparableValue(arguments.get(userOverride.getKey()));
+ String overrideValue = normalizeComparableValue(userOverride.getValue());
if (!actualValue.isEmpty() && !overrideValue.isEmpty() && !actualValue.equals(overrideValue)) {
- conflicts.add(String.format("%s conflicts with %s.%s", entry.getKey(), WorkflowFieldNames.USER_OVERRIDES, entry.getKey()));
+ conflicts.add(String.format("%s conflicts with %s.%s", userOverride.getKey(), WorkflowFieldNames.USER_OVERRIDES, userOverride.getKey()));
}
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtils.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtils.java
index f0963a7f01b..986a17f94f7 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtils.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtils.java
@@ -200,9 +200,9 @@ public final class WorkflowSQLUtils {
return result;
}
- private static int findPropertySeparatorIndex(final String entry) {
- int equalsIndex = entry.indexOf('=');
- return -1 == equalsIndex ? entry.indexOf(':') : equalsIndex;
+ private static int findPropertySeparatorIndex(final String propertyEntry) {
+ int equalsIndex = propertyEntry.indexOf('=');
+ return -1 == equalsIndex ? propertyEntry.indexOf(':') : equalsIndex;
}
private static String stripQuotes(final String value) {
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupport.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupport.java
index b071cdee585..898f9c77b3f 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupport.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupport.java
@@ -199,14 +199,14 @@ public final class WorkflowValidationSupport {
return result;
}
- private void addValidationSection(final List<Map<String, Object>> result, final String layer, final ValidationSection section) {
+ private void addValidationSection(final List<Map<String, Object>> sections, final String layer, final ValidationSection section) {
if (null == section) {
return;
}
Map<String, Object> sectionPayload = new LinkedHashMap<>(4, 1F);
sectionPayload.put("layer", layer);
sectionPayload.putAll(section.toMap());
- result.add(sectionPayload);
+ sections.add(sectionPayload);
}
private String resolveValidationStatus(final ValidationReport validationReport) {
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoaderTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoaderTest.java
index bd804c3efbf..5cef21160de 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoaderTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoaderTest.java
@@ -432,7 +432,7 @@ class MCPJdbcMetadataLoaderTest {
ResultSet viewResultSet = mockResultSet("TABLE_NAME");
ResultSet columnResultSet = mockResultSet("COLUMN_NAME", "order_id");
ResultSet indexResultSet = mockResultSet("INDEX_NAME");
- final ResultSet emptyQueryResultSet = mockMultiRowResultSet(List.of());
+ ResultSet emptyQueryResultSet = mockMultiRowResultSet(List.of());
when(result.getMetaData()).thenReturn(databaseMetaData);
when(result.createStatement()).thenReturn(statement);
when(databaseMetaData.getDatabaseProductName()).thenReturn(databaseProductName);
@@ -445,7 +445,7 @@ class MCPJdbcMetadataLoaderTest {
when(databaseMetaData.getColumns(isNull(), isNull(), eq("orders"), eq("%"))).thenReturn(columnResultSet);
when(databaseMetaData.getIndexInfo(isNull(), isNull(), eq("orders"), eq(false), eq(false))).thenReturn(indexResultSet);
for (Entry<String, String> entry : scalarQueries.entrySet()) {
- final ResultSet scalarResultSet = mockScalarResultSet(entry.getValue());
+ ResultSet scalarResultSet = mockScalarResultSet(entry.getValue());
when(statement.executeQuery(entry.getKey())).thenReturn(scalarResultSet);
}
return result;
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/security/MCPClientSafetyPolicyTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/security/MCPClientSafetyPolicyTest.java
index 9eefce428d3..5d3f6bbd1ed 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/security/MCPClientSafetyPolicyTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/security/MCPClientSafetyPolicyTest.java
@@ -29,7 +29,7 @@ class MCPClientSafetyPolicyTest {
@Test
void assertGetMaxToolCallsPerSession() {
- final String previous = System.getProperty(MCPClientSafetyPolicy.MAX_TOOL_CALLS_PER_SESSION_PROPERTY);
+ String previous = System.getProperty(MCPClientSafetyPolicy.MAX_TOOL_CALLS_PER_SESSION_PROPERTY);
try {
System.setProperty(MCPClientSafetyPolicy.MAX_TOOL_CALLS_PER_SESSION_PROPERTY, "8");
assertThat(MCPClientSafetyPolicy.getMaxToolCallsPerSession(), is(8));
@@ -42,10 +42,10 @@ class MCPClientSafetyPolicyTest {
@Test
void assertCreateModelFacingPayload() {
- final Map<String, Object> actual = MCPClientSafetyPolicy.createModelFacingPayload();
+ Map<String, Object> actual = MCPClientSafetyPolicy.createModelFacingPayload();
assertThat(actual.get("identity_scope"), is("mcp_session"));
assertTrue(String.valueOf(actual.get("external_model_boundary")).contains("never calls external model providers"));
- final Map<?, ?> actualToolCallLimit = (Map<?, ?>) actual.get("tool_call_limit");
+ Map<?, ?> actualToolCallLimit = (Map<?, ?>) actual.get("tool_call_limit");
assertThat(actualToolCallLimit.get("scope"), is("session"));
assertThat(actualToolCallLimit.get("property"), is(MCPClientSafetyPolicy.MAX_TOOL_CALLS_PER_SESSION_PROPERTY));
}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupportTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupportTest.java
index 3cbf24a4e90..207cb807b77 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupportTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupportTest.java
@@ -35,6 +35,7 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
@@ -110,7 +111,7 @@ class WorkflowPlanningSupportTest {
void assertPrepareSnapshot() {
WorkflowContextSnapshot snapshot = new WorkflowContextSnapshot();
snapshot.setPlanId("plan-1");
- snapshot.getIssues().add(new WorkflowIssue(WorkflowIssueCode.DATABASE_REQUIRED, "error", "intaking", "message", "action", true, java.util.Map.of()));
+ snapshot.getIssues().add(new WorkflowIssue(WorkflowIssueCode.DATABASE_REQUIRED, "error", "intaking", "message", "action", true, Map.of()));
WorkflowRequest request = new WorkflowRequest();
request.setDatabase("logic_db");
ClarifiedIntent clarifiedIntent = new ClarifiedIntent();
@@ -156,7 +157,7 @@ class WorkflowPlanningSupportTest {
void assertHasBlockingAlgorithmIssuesAddsFallbackQuestion() {
ClarifiedIntent clarifiedIntent = new ClarifiedIntent();
WorkflowContextSnapshot snapshot = new WorkflowContextSnapshot();
- snapshot.getIssues().add(new WorkflowIssue(WorkflowIssueCode.ALGORITHM_NOT_FOUND, "error", "selecting-algorithm", "message", "action", false, java.util.Map.of()));
+ snapshot.getIssues().add(new WorkflowIssue(WorkflowIssueCode.ALGORITHM_NOT_FOUND, "error", "selecting-algorithm", "message", "action", false, Map.of()));
boolean actual = planningSupport.hasBlockingAlgorithmIssues(clarifiedIntent, snapshot, "Please use an algorithm visible in the current Proxy.");
assertTrue(actual);
assertThat(clarifiedIntent.getClarificationMessages(), is(List.of("Please use an algorithm visible in the current Proxy.")));
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/config/LLME2EConfiguration.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/config/LLME2EConfiguration.java
index c1a7f45b6ae..5afacce9659 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/config/LLME2EConfiguration.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/config/LLME2EConfiguration.java
@@ -80,7 +80,7 @@ public final class LLME2EConfiguration {
* @return LLM E2E configuration
*/
public static LLME2EConfiguration load() {
- final RuntimeMode runtimeMode = RuntimeMode.from(readString("mcp.llm.runtime-mode", "MCP_LLM_RUNTIME_MODE", RuntimeMode.DOCKER.getValue()));
+ RuntimeMode runtimeMode = RuntimeMode.from(readString("mcp.llm.runtime-mode", "MCP_LLM_RUNTIME_MODE", RuntimeMode.DOCKER.getValue()));
return new LLME2EConfiguration(
normalizeBaseUrl(readString("mcp.llm.base-url", "MCP_LLM_BASE_URL", DEFAULT_BASE_URL)),
readString("mcp.llm.provider", "MCP_LLM_PROVIDER", "openai-compatible"),
@@ -104,7 +104,7 @@ public final class LLME2EConfiguration {
* @throws IOException IO exception
*/
public Path createArtifactDirectory(final String scenarioId) throws IOException {
- final Path result = artifactRoot.resolve(runId).resolve(scenarioId);
+ Path result = artifactRoot.resolve(runId).resolve(scenarioId);
Files.createDirectories(result);
return result;
}
@@ -172,7 +172,7 @@ public final class LLME2EConfiguration {
}
private static int readInteger(final String propertyName, final String environmentName, final int defaultValue) {
- final String result = readString(propertyName, environmentName, String.valueOf(defaultValue));
+ String result = readString(propertyName, environmentName, String.valueOf(defaultValue));
try {
return Integer.parseInt(result);
} catch (final NumberFormatException ignored) {
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPActionExecutor.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPActionExecutor.java
index f6773a3dbe8..d137b203287 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPActionExecutor.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPActionExecutor.java
@@ -126,12 +126,12 @@ final class LLMMCPActionExecutor {
return "";
}
- private void normalizeCompletionReferenceType(final Map<String, Object> result) {
- String referenceType = Objects.toString(result.get("type"), "").trim().toLowerCase(Locale.ENGLISH);
+ private void normalizeCompletionReferenceType(final Map<String, Object> completionReference) {
+ String referenceType = Objects.toString(completionReference.get("type"), "").trim().toLowerCase(Locale.ENGLISH);
if ("prompt".equals(referenceType)) {
- result.put("type", "ref/prompt");
+ completionReference.put("type", "ref/prompt");
} else if ("resource".equals(referenceType)) {
- result.put("type", "ref/resource");
+ completionReference.put("type", "ref/resource");
}
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunner.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunner.java
index 90455d25240..f23acfb41df 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunner.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunner.java
@@ -36,6 +36,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
/**
* LLM MCP conversation runner.
@@ -89,8 +90,8 @@ public final class LLMMCPConversationRunner {
* @return LLM E2E artifact bundle
*/
public LLME2EArtifactBundle run(final LLME2EScenario scenario) {
- final List<LLMChatMessage> messages = createInitialMessages(scenario);
- final LLMMCPConversationArtifacts artifacts = new LLMMCPConversationArtifacts(modelProvider, modelName);
+ List<LLMChatMessage> messages = createInitialMessages(scenario);
+ LLMMCPConversationArtifacts artifacts = new LLMMCPConversationArtifacts(modelProvider, modelName);
try {
llmChatClient.waitUntilReady();
openInteractionClient();
@@ -99,11 +100,11 @@ public final class LLMMCPConversationRunner {
int finalAnswerAttempts = 0;
for (int turnIndex = 0; turnIndex < maxTurns; turnIndex++) {
finalAnswerRequested = requestFinalAnswerIfReady(scenario, messages, artifacts, finalAnswerRequested);
- final List<String> turnToolNames = finalAnswerRequested ? List.of() : createTurnToolNames(scenario, artifacts.getInteractionTrace());
- final LLMChatCompletion completion = completeTurn(scenario, messages, artifacts, finalAnswerRequested, turnToolNames);
- final LLME2EArtifactBundle toolCallFailure = processToolCallCompletion(scenario, completion, messages, artifacts, turnToolNames);
- if (null != toolCallFailure) {
- return toolCallFailure;
+ List<String> turnToolNames = finalAnswerRequested ? List.of() : createTurnToolNames(scenario, artifacts.getInteractionTrace());
+ LLMChatCompletion completion = completeTurn(scenario, messages, artifacts, finalAnswerRequested, turnToolNames);
+ Optional<LLME2EArtifactBundle> toolCallFailure = processToolCallCompletion(scenario, completion, messages, artifacts, turnToolNames);
+ if (toolCallFailure.isPresent()) {
+ return toolCallFailure.get();
}
if (!completion.getToolCalls().isEmpty()) {
continue;
@@ -114,7 +115,7 @@ public final class LLMMCPConversationRunner {
}
continue;
}
- final FinalAnswerHandlingResult finalAnswerHandlingResult = handleFinalAnswerCompletion(scenario, completion, messages, artifacts, finalAnswerAttempts);
+ FinalAnswerHandlingResult finalAnswerHandlingResult = handleFinalAnswerCompletion(scenario, completion, messages, artifacts, finalAnswerAttempts);
finalAnswerAttempts = finalAnswerHandlingResult.finalAnswerAttempts();
if (finalAnswerHandlingResult.retryRequested()) {
continue;
@@ -129,7 +130,7 @@ public final class LLMMCPConversationRunner {
Thread.currentThread().interrupt();
return createFailureBundle(scenario, artifacts, "model_service_unavailable", "Conversation was interrupted.");
} catch (final IllegalStateException ex) {
- final String failureType = ex.getMessage().startsWith("MCP") || ex.getMessage().startsWith("Failed to initialize MCP")
+ String failureType = ex.getMessage().startsWith("MCP") || ex.getMessage().startsWith("Failed to initialize MCP")
? "mcp_runtime_unavailable"
: "model_service_unavailable";
return createFailureBundle(scenario, artifacts, failureType, ex.getMessage());
@@ -177,7 +178,7 @@ public final class LLMMCPConversationRunner {
}
private boolean isExpectedSchema(final LLMStructuredAnswer expectedAnswer, final Map<String, Object> arguments) {
- final String schema = Objects.toString(arguments.get("schema"), "").trim();
+ String schema = Objects.toString(arguments.get("schema"), "").trim();
return expectedAnswer.getSchema().equals(schema) || schema.isEmpty();
}
@@ -190,8 +191,8 @@ public final class LLMMCPConversationRunner {
private LLMChatCompletion completeTurn(final LLME2EScenario scenario, final List<LLMChatMessage> messages, final LLMMCPConversationArtifacts artifacts,
final boolean finalAnswerRequested, final List<String> turnToolNames) throws IOException, InterruptedException {
- final String toolChoice = createToolChoice(scenario, artifacts, finalAnswerRequested);
- final LLMChatCompletion result = llmChatClient.complete(finalAnswerRequested ? createFinalAnswerMessages(scenario, messages, artifacts.getInteractionTrace()) : messages,
+ String toolChoice = createToolChoice(scenario, artifacts, finalAnswerRequested);
+ LLMChatCompletion result = llmChatClient.complete(finalAnswerRequested ? createFinalAnswerMessages(scenario, messages, artifacts.getInteractionTrace()) : messages,
finalAnswerRequested ? List.of() : toolDefinitionFactory.create(turnToolNames),
toolChoice, finalAnswerRequested);
artifacts.addRawModelOutput(result.getRawResponse());
@@ -200,18 +201,18 @@ public final class LLMMCPConversationRunner {
private List<String> createTurnToolNames(final LLME2EScenario scenario, final List<MCPInteractionTraceRecord> interactionTrace) {
if (!interactionTrace.isEmpty()) {
- final String immediateActionName = findImmediateNextActionName(interactionTrace.getLast());
+ String immediateActionName = findImmediateNextActionName(interactionTrace.getLast());
if (!immediateActionName.isEmpty() && scenario.getAllowedToolNames().contains(immediateActionName)) {
return List.of(immediateActionName);
}
}
if (hasSideEffectExecutionNextAction(interactionTrace)) {
- final List<String> readOnlyToolNames = findMissingReadOnlyToolNames(scenario, interactionTrace);
+ List<String> readOnlyToolNames = findMissingReadOnlyToolNames(scenario, interactionTrace);
if (!readOnlyToolNames.isEmpty()) {
return List.of(readOnlyToolNames.get(0));
}
}
- final List<String> missingToolNames = findMissingAllowedToolNames(scenario, interactionTrace);
+ List<String> missingToolNames = findMissingAllowedToolNames(scenario, interactionTrace);
if (!missingToolNames.isEmpty()) {
return List.of(missingToolNames.get(0));
}
@@ -219,7 +220,7 @@ public final class LLMMCPConversationRunner {
}
private List<String> findMissingAllowedToolNames(final LLME2EScenario scenario, final List<MCPInteractionTraceRecord> interactionTrace) {
- final List<String> result = new LinkedList<>();
+ List<String> result = new LinkedList<>();
for (String each : LLMMCPInteractionCoverage.findMissingRequiredInteractionNames(scenario.getRequiredToolNames(), interactionTrace)) {
if (scenario.getAllowedToolNames().contains(each)) {
result.add(each);
@@ -229,7 +230,7 @@ public final class LLMMCPConversationRunner {
}
private List<String> findMissingReadOnlyToolNames(final LLME2EScenario scenario, final List<MCPInteractionTraceRecord> interactionTrace) {
- final List<String> result = new LinkedList<>();
+ List<String> result = new LinkedList<>();
for (String each : LLMMCPInteractionCoverage.findMissingRequiredInteractionNames(scenario.getRequiredToolNames(), interactionTrace)) {
if (scenario.getAllowedToolNames().contains(each) && isReadOnlyToolName(each)) {
result.add(each);
@@ -255,55 +256,55 @@ public final class LLMMCPConversationRunner {
return LLMMCPInteractionCoverage.hasRequiredInteractionCoverage(scenario.getRequiredToolNames(), artifacts.getInteractionTrace()) ? "auto" : "required";
}
- private LLME2EArtifactBundle processToolCallCompletion(final LLME2EScenario scenario, final LLMChatCompletion completion,
- final List<LLMChatMessage> messages, final LLMMCPConversationArtifacts artifacts,
- final List<String> turnToolNames) throws InterruptedException {
+ private Optional<LLME2EArtifactBundle> processToolCallCompletion(final LLME2EScenario scenario, final LLMChatCompletion completion,
+ final List<LLMChatMessage> messages, final LLMMCPConversationArtifacts artifacts,
+ final List<String> turnToolNames) throws InterruptedException {
if (completion.getToolCalls().isEmpty()) {
- return null;
+ return Optional.empty();
}
messages.add(LLMChatMessage.assistant(completion.getContent(), completion.getToolCalls()));
for (LLMToolCall each : completion.getToolCalls()) {
- final LLME2EArtifactBundle result = processToolCall(scenario, each, messages, artifacts, turnToolNames);
- if (null != result) {
+ Optional<LLME2EArtifactBundle> result = processToolCall(scenario, each, messages, artifacts, turnToolNames);
+ if (result.isPresent()) {
return result;
}
}
addTraceDrivenInstruction(scenario, messages, artifacts.getInteractionTrace());
- return null;
+ return Optional.empty();
}
- private LLME2EArtifactBundle processToolCall(final LLME2EScenario scenario, final LLMToolCall toolCall, final List<LLMChatMessage> messages,
- final LLMMCPConversationArtifacts artifacts, final List<String> turnToolNames) throws InterruptedException {
+ private Optional<LLME2EArtifactBundle> processToolCall(final LLME2EScenario scenario, final LLMToolCall toolCall, final List<LLMChatMessage> messages,
+ final LLMMCPConversationArtifacts artifacts, final List<String> turnToolNames) throws InterruptedException {
if (!scenario.getAllowedToolNames().contains(toolCall.getName())) {
artifacts.addInteractionTrace(MCPInteractionTraceRecord.createInvalidAction(artifacts.nextSequence(), "tool_call", toolCall.getName(),
Map.of("rawArgumentsJson", toolCall.getArgumentsJson()), "unexpected_tool_requested"));
- return createFailureBundle(scenario, artifacts, "unexpected_tool_requested", "Model requested an unsupported tool.");
+ return Optional.of(createFailureBundle(scenario, artifacts, "unexpected_tool_requested", "Model requested an unsupported tool."));
}
try {
- final Map<String, Object> rawArguments = LLMMCPJsonValues.parseToolArguments(toolCall.getArgumentsJson());
- final List<String> currentToolNames = artifacts.getInteractionTrace().isEmpty()
+ Map<String, Object> rawArguments = LLMMCPJsonValues.parseToolArguments(toolCall.getArgumentsJson());
+ List<String> currentToolNames = artifacts.getInteractionTrace().isEmpty()
? turnToolNames
: createTurnToolNames(scenario, artifacts.getInteractionTrace());
- final LLMMCPToolCallNormalizer.NormalizedToolCall normalizedToolCall = LLMMCPToolCallNormalizer.normalize(scenario, toolCall.getName(), rawArguments, currentToolNames,
+ LLMMCPToolCallNormalizer.NormalizedToolCall normalizedToolCall = LLMMCPToolCallNormalizer.normalize(scenario, toolCall.getName(), rawArguments, currentToolNames,
artifacts.getInteractionTrace());
return processToolCall(scenario, toolCall, messages, artifacts, rawArguments, normalizedToolCall.arguments(), normalizedToolCall.name());
} catch (final IllegalArgumentException ex) {
artifacts.addInteractionTrace(MCPInteractionTraceRecord.createInvalidAction(artifacts.nextSequence(), "tool_call", toolCall.getName(),
Map.of("rawArgumentsJson", toolCall.getArgumentsJson()), "invalid_tool_arguments"));
- return createFailureBundle(scenario, artifacts, "invalid_tool_arguments", "Model returned invalid tool arguments JSON.");
+ return Optional.of(createFailureBundle(scenario, artifacts, "invalid_tool_arguments", "Model returned invalid tool arguments JSON."));
}
}
- private LLME2EArtifactBundle processToolCall(final LLME2EScenario scenario, final LLMToolCall toolCall, final List<LLMChatMessage> messages,
- final LLMMCPConversationArtifacts artifacts, final Map<String, Object> rawArguments,
- final Map<String, Object> arguments, final String toolName) throws InterruptedException {
- final LLME2EArtifactBundle validationFailure = validateToolCall(scenario, toolName, arguments, artifacts);
- if (null != validationFailure) {
+ private Optional<LLME2EArtifactBundle> processToolCall(final LLME2EScenario scenario, final LLMToolCall toolCall, final List<LLMChatMessage> messages,
+ final LLMMCPConversationArtifacts artifacts, final Map<String, Object> rawArguments,
+ final Map<String, Object> arguments, final String toolName) throws InterruptedException {
+ Optional<LLME2EArtifactBundle> validationFailure = validateToolCall(scenario, toolName, arguments, artifacts);
+ if (validationFailure.isPresent()) {
return validationFailure;
}
- final long startTime = System.currentTimeMillis();
- final Map<String, Object> response = actionExecutor.executeSafely(toolName, arguments);
- final long latencyMillis = System.currentTimeMillis() - startTime;
+ long startTime = System.currentTimeMillis();
+ Map<String, Object> response = actionExecutor.executeSafely(toolName, arguments);
+ long latencyMillis = System.currentTimeMillis() - startTime;
artifacts.addRuntimeLogLine("action=" + toolName + " args=" + JsonUtils.toJsonString(arguments));
artifacts.addRuntimeLogLine("response=" + JsonUtils.toJsonString(response));
String actionOrigin = toolCall.getName().equals(toolName) && rawArguments.equals(arguments)
@@ -311,19 +312,19 @@ public final class LLMMCPConversationRunner {
: MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN;
artifacts.addInteractionTrace(createTraceRecord(artifacts.nextSequence(), toolName, actionOrigin, arguments, response, latencyMillis));
messages.add(LLMChatMessage.tool(toolCall.getId(), LLMMCPModelFacingToolResponseFormatter.format(response)));
- return null;
+ return Optional.empty();
}
private void addTraceDrivenInstruction(final LLME2EScenario scenario, final List<LLMChatMessage> messages, final List<MCPInteractionTraceRecord> interactionTrace) {
if (interactionTrace.isEmpty()) {
return;
}
- final String resourceReadInstruction = createResourceReadInstruction(scenario, interactionTrace);
+ String resourceReadInstruction = createResourceReadInstruction(scenario, interactionTrace);
if (!resourceReadInstruction.isEmpty()) {
messages.add(LLMChatMessage.user(resourceReadInstruction));
return;
}
- final String immediateNextActionInstruction = createImmediateNextActionInstruction(interactionTrace.getLast());
+ String immediateNextActionInstruction = createImmediateNextActionInstruction(interactionTrace.getLast());
if (!immediateNextActionInstruction.isEmpty()) {
messages.add(LLMChatMessage.user(immediateNextActionInstruction));
return;
@@ -399,7 +400,7 @@ public final class LLMMCPConversationRunner {
|| !shouldPromptExactResourceRead(interactionTrace.getLast())) {
return "";
}
- final String resourceUri = LLMMCPScenarioInference.findExpectedResourceUri(scenario);
+ String resourceUri = LLMMCPScenarioInference.findExpectedResourceUri(scenario);
return resourceUri.isEmpty() || hasReadResource(resourceUri, interactionTrace)
? ""
: String.format(Locale.ENGLISH,
@@ -425,31 +426,30 @@ public final class LLMMCPConversationRunner {
return !interactionTrace.isEmpty() && !createImmediateNextActionInstruction(interactionTrace.getLast()).isEmpty();
}
- private LLME2EArtifactBundle validateToolCall(final LLME2EScenario scenario, final String toolName, final Map<String, Object> arguments,
- final LLMMCPConversationArtifacts artifacts) {
+ private Optional<LLME2EArtifactBundle> validateToolCall(final LLME2EScenario scenario, final String toolName, final Map<String, Object> arguments,
+ final LLMMCPConversationArtifacts artifacts) {
return safetyValidator.validate(toolName, arguments)
.map(each -> {
artifacts.addInteractionTrace(MCPInteractionTraceRecord.createInvalidAction(artifacts.nextSequence(), each.traceKind(), toolName, arguments,
each.failureType()));
return createFailureBundle(scenario, artifacts, each.failureType(), each.message());
- })
- .orElse(null);
+ });
}
private FinalAnswerHandlingResult handleFinalAnswerCompletion(final LLME2EScenario scenario, final LLMChatCompletion completion, final List<LLMChatMessage> messages,
final LLMMCPConversationArtifacts artifacts, final int finalAnswerAttempts) {
- final int actualAttempts = finalAnswerAttempts + 1;
+ int actualAttempts = finalAnswerAttempts + 1;
artifacts.setFinalAnswerJson(completion.getContent());
try {
- final LLMStructuredAnswer actualAnswer = LLMStructuredAnswer.fromJson(artifacts.getFinalAnswerJson());
- return FinalAnswerHandlingResult.complete(actualAttempts,
+ LLMStructuredAnswer actualAnswer = LLMStructuredAnswer.fromJson(artifacts.getFinalAnswerJson());
+ return new FinalAnswerHandlingResult(actualAttempts, false,
artifacts.createArtifactBundle(scenario, finalAnswerValidator.validateSafely(scenario, actualAnswer, artifacts.getInteractionTrace())));
} catch (final IllegalArgumentException ex) {
if (1 >= actualAttempts) {
messages.add(LLMChatMessage.user("Return valid JSON only. Do not include markdown or explanation."));
- return FinalAnswerHandlingResult.retry(actualAttempts);
+ return new FinalAnswerHandlingResult(actualAttempts, true, null);
}
- return FinalAnswerHandlingResult.complete(actualAttempts,
+ return new FinalAnswerHandlingResult(actualAttempts, false,
createFailureBundle(scenario, artifacts, "invalid_final_json", "Model did not return a valid final JSON payload."));
}
}
@@ -489,10 +489,10 @@ public final class LLMMCPConversationRunner {
private List<LLMChatMessage> createFinalAnswerMessages(final LLME2EScenario scenario, final List<LLMChatMessage> messages,
final List<MCPInteractionTraceRecord> interactionTrace) {
- final List<LLMChatMessage> result = new LinkedList<>();
+ List<LLMChatMessage> result = new LinkedList<>();
result.add(LLMChatMessage.system("Return the final MCP assessment answer as valid JSON only."));
result.add(LLMChatMessage.user(createFinalAnswerInstruction(scenario, interactionTrace)));
- final String retryInstruction = findFinalAnswerRetryInstruction(messages);
+ String retryInstruction = findFinalAnswerRetryInstruction(messages);
if (!retryInstruction.isEmpty()) {
result.add(LLMChatMessage.user(retryInstruction));
}
@@ -501,7 +501,7 @@ public final class LLMMCPConversationRunner {
private String findFinalAnswerRetryInstruction(final List<LLMChatMessage> messages) {
for (int index = messages.size() - 1; index >= 0; index--) {
- final String content = messages.get(index).getContent();
+ String content = messages.get(index).getContent();
if (content.contains("Return valid JSON only.")) {
return content;
}
@@ -513,10 +513,10 @@ public final class LLMMCPConversationRunner {
}
private String createFinalAnswerInstruction(final LLME2EScenario scenario, final List<MCPInteractionTraceRecord> interactionTrace) {
- final LLMStructuredAnswer expectedAnswer = scenario.getExpectedAnswer();
- final String interactionSequence = JsonUtils.toJsonString(createComparableInteractionSequence(interactionTrace));
- final String totalOrders = findLatestTotalOrders(interactionTrace);
- final String prompt = "Return JSON only with keys database, schema, table, query, totalOrders, interactionSequence. "
+ LLMStructuredAnswer expectedAnswer = scenario.getExpectedAnswer();
+ String interactionSequence = JsonUtils.toJsonString(createComparableInteractionSequence(interactionTrace));
+ String totalOrders = findLatestTotalOrders(interactionTrace);
+ String prompt = "Return JSON only with keys database, schema, table, query, totalOrders, interactionSequence. "
+ "Use database `%s`, schema `%s`, table `%s`, and query `%s`; set totalOrders to `%s` from the latest successful database_gateway_execute_query result. "
+ "Set interactionSequence exactly to this JSON array: %s. "
+ "Do not add inferred, expected, available, or failed MCP action names. Required tools are `%s`.";
@@ -528,9 +528,9 @@ public final class LLMMCPConversationRunner {
private String findLatestTotalOrders(final List<MCPInteractionTraceRecord> interactionTrace) {
for (int index = interactionTrace.size() - 1; index >= 0; index--) {
- final MCPInteractionTraceRecord each = interactionTrace.get(index);
+ MCPInteractionTraceRecord each = interactionTrace.get(index);
if ("database_gateway_execute_query".equals(each.getTargetName()) && each.isValid()) {
- final String result = findTotalOrdersInRowObjects(each.getStructuredContent());
+ String result = findTotalOrdersInRowObjects(each.getStructuredContent());
return result.isEmpty() ? findTotalOrdersInRows(each.getStructuredContent()) : result;
}
}
@@ -538,7 +538,7 @@ public final class LLMMCPConversationRunner {
}
private String findTotalOrdersInRowObjects(final Map<String, Object> structuredContent) {
- final List<Map<String, Object>> rowObjects = LLMMCPJsonValues.castToList(structuredContent.get("row_objects"));
+ List<Map<String, Object>> rowObjects = LLMMCPJsonValues.castToList(structuredContent.get("row_objects"));
if (rowObjects.isEmpty()) {
return "";
}
@@ -546,11 +546,11 @@ public final class LLMMCPConversationRunner {
}
private String findTotalOrdersInRows(final Map<String, Object> structuredContent) {
- final List<Object> rows = LLMMCPJsonValues.castToList(structuredContent.get("rows"));
+ List<Object> rows = LLMMCPJsonValues.castToList(structuredContent.get("rows"));
if (rows.isEmpty()) {
return "";
}
- final List<Object> row = LLMMCPJsonValues.castToList(rows.get(0));
+ List<Object> row = LLMMCPJsonValues.castToList(rows.get(0));
return row.isEmpty() ? "" : Objects.toString(row.get(0), "").trim();
}
@@ -565,20 +565,20 @@ public final class LLMMCPConversationRunner {
}
private String createRequiredToolCallInstruction(final LLME2EScenario scenario, final LLMMCPConversationArtifacts artifacts) {
- final List<String> missingToolNames = LLMMCPInteractionCoverage.findMissingRequiredInteractionNames(scenario.getRequiredToolNames(), artifacts.getInteractionTrace());
- final LLMStructuredAnswer expectedAnswer = scenario.getExpectedAnswer();
- final String previewInstruction = missingToolNames.contains("database_gateway_execute_update")
+ List<String> missingToolNames = LLMMCPInteractionCoverage.findMissingRequiredInteractionNames(scenario.getRequiredToolNames(), artifacts.getInteractionTrace());
+ LLMStructuredAnswer expectedAnswer = scenario.getExpectedAnswer();
+ String previewInstruction = missingToolNames.contains("database_gateway_execute_update")
? String.format(Locale.ENGLISH,
" For database_gateway_execute_update, set database `%s`, schema `%s`, execution_mode=preview, and keep the side-effecting SQL unchanged; do not use execution_mode=execute.",
expectedAnswer.getDatabase(), expectedAnswer.getSchema())
: "";
- final String resourceInstruction = missingToolNames.contains(MCPInteractionActionNames.READ_RESOURCE)
+ String resourceInstruction = missingToolNames.contains(MCPInteractionActionNames.READ_RESOURCE)
? " For mcp_read_resource, use an exact shardingsphere:// URI from the user request or the latest tool response; do not invent abbreviated URI strings."
: "";
- final String planningInstruction = hasMissingPlanningTool(missingToolNames)
+ String planningInstruction = hasMissingPlanningTool(missingToolNames)
? " For a new database_gateway_plan_* call, omit plan_id unless a previous MCP planning response returned an actual plan_id."
: "";
- final String workflowPlanInstruction = createWorkflowPlanInstruction(missingToolNames, artifacts.getInteractionTrace());
+ String workflowPlanInstruction = createWorkflowPlanInstruction(missingToolNames, artifacts.getInteractionTrace());
return String.format(Locale.ENGLISH,
"Required MCP tool coverage is incomplete. Remaining required MCP tools: %s. "
+ "Call one remaining tool as an actual MCP tool_call now; do not answer in text, do not write JSON, and do not write <tool_call> markup. "
@@ -645,13 +645,5 @@ public final class LLMMCPConversationRunner {
}
private record FinalAnswerHandlingResult(int finalAnswerAttempts, boolean retryRequested, LLME2EArtifactBundle artifactBundle) {
-
- private static FinalAnswerHandlingResult retry(final int finalAnswerAttempts) {
- return new FinalAnswerHandlingResult(finalAnswerAttempts, true, null);
- }
-
- private static FinalAnswerHandlingResult complete(final int finalAnswerAttempts, final LLME2EArtifactBundle artifactBundle) {
- return new FinalAnswerHandlingResult(finalAnswerAttempts, false, artifactBundle);
- }
}
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerCoverageTest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerCoverageTest.java
index e6061d10f99..1e529aea73e 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerCoverageTest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerCoverageTest.java
@@ -23,7 +23,8 @@ import org.apache.shardingsphere.test.e2e.mcp.llm.conversation.client.LLMChatMes
import org.apache.shardingsphere.test.e2e.mcp.llm.scenario.LLME2EScenario;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionActionNames;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionTraceRecord;
-import org.hamcrest.CoreMatchers;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
@@ -46,155 +47,155 @@ class LLMMCPConversationRunnerCoverageTest extends AbstractLLMMCPConversationRun
@Test
void assertRunWithNonNumericExecuteQueryTrace() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"));
when(getMCPInteractionClient().call("database_gateway_execute_query", executeQueryArguments)).thenReturn(createResultSetPayload("NaN"));
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of("database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("unexpected_query_result"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("unexpected_query_result"));
}
@Test
void assertRunWithNonResultSetExecuteQueryTrace() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"));
when(getMCPInteractionClient().call("database_gateway_execute_query", executeQueryArguments)).thenReturn(Map.of("result_kind", "update_count", "update_count", 1));
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of("database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("unexpected_query_result"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("unexpected_query_result"));
}
@Test
void assertRunWithMissingRequiredToolCoverage() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(0));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
+ assertThat(actual.getInteractionTrace().size(), is(0));
verify(getMCPInteractionClient()).open();
verify(getMCPInteractionClient()).close();
}
@Test
void assertRunNamesRemainingToolsWhenCoverageIsMissing() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response-1"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
verify(getLLMChatClient(), times(2)).complete(actualMessages.capture(), anyList(), eq("required"), eq(false));
- final List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
+ List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(),
- CoreMatchers.containsString("Remaining required MCP tools: database_gateway_execute_query"));
- assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(), CoreMatchers.containsString("actual MCP tool_call"));
- assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(), CoreMatchers.containsString("database `logic_db`"));
- assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(), CoreMatchers.containsString("schema `public`"));
- assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(), CoreMatchers.containsString("sql `SELECT COUNT(*) AS total_orders FROM orders`"));
+ containsString("Remaining required MCP tools: database_gateway_execute_query"));
+ assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(), containsString("actual MCP tool_call"));
+ assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(), containsString("database `logic_db`"));
+ assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(), containsString("schema `public`"));
+ assertThat(actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent(), containsString("sql `SELECT COUNT(*) AS total_orders FROM orders`"));
}
@Test
void assertRunNamesExactResourceUriWhenResourceCoverageIsMissing() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.READ_RESOURCE));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
+ LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.READ_RESOURCE));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response-1"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
verify(getLLMChatClient(), times(2)).complete(actualMessages.capture(), anyList(), eq("required"), eq(false));
- final List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
- final String actualRetryInstruction = actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent();
- assertThat(actualRetryInstruction, CoreMatchers.containsString("Remaining required MCP tools: mcp_read_resource"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("exact shardingsphere:// URI"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("do not invent abbreviated URI strings"));
+ List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
+ String actualRetryInstruction = actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent();
+ assertThat(actualRetryInstruction, containsString("Remaining required MCP tools: mcp_read_resource"));
+ assertThat(actualRetryInstruction, containsString("exact shardingsphere:// URI"));
+ assertThat(actualRetryInstruction, containsString("do not invent abbreviated URI strings"));
}
@Test
void assertRunNamesPreviewModeWhenUpdateCoverageIsMissing() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_update"));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_update"));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response-1"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
verify(getLLMChatClient(), times(2)).complete(actualMessages.capture(), anyList(), eq("required"), eq(false));
- final List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
- final String actualRetryInstruction = actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent();
- assertThat(actualRetryInstruction, CoreMatchers.containsString("Remaining required MCP tools: database_gateway_execute_update"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("database `logic_db`"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("schema `public`"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("execution_mode=preview"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("do not use execution_mode=execute"));
+ List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
+ String actualRetryInstruction = actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent();
+ assertThat(actualRetryInstruction, containsString("Remaining required MCP tools: database_gateway_execute_update"));
+ assertThat(actualRetryInstruction, containsString("database `logic_db`"));
+ assertThat(actualRetryInstruction, containsString("schema `public`"));
+ assertThat(actualRetryInstruction, containsString("execution_mode=preview"));
+ assertThat(actualRetryInstruction, containsString("do not use execution_mode=execute"));
}
@Test
void assertRunNamesPlanIdRuleWhenPlanningCoverageIsMissing() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_plan_mask_rule"));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_plan_mask_rule"));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response-1"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
verify(getLLMChatClient(), times(2)).complete(actualMessages.capture(), anyList(), eq("required"), eq(false));
- final List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
- final String actualRetryInstruction = actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent();
- assertThat(actualRetryInstruction, CoreMatchers.containsString("Remaining required MCP tools: database_gateway_plan_mask_rule"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("For a new database_gateway_plan_* call"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("omit plan_id"));
+ List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
+ String actualRetryInstruction = actualSecondTurnMessages.get(actualSecondTurnMessages.size() - 1).getContent();
+ assertThat(actualRetryInstruction, containsString("Remaining required MCP tools: database_gateway_plan_mask_rule"));
+ assertThat(actualRetryInstruction, containsString("For a new database_gateway_plan_* call"));
+ assertThat(actualRetryInstruction, containsString("omit plan_id"));
}
@Test
void assertRunRemovesInitialPlanningPlanId() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_plan_mask_rule"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
- final Map<String, Object> rawPlanArguments = Map.of("plan_id", "1", "database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
- final Map<String, Object> expectedPlanArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_plan_mask_rule"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
+ Map<String, Object> rawPlanArguments = Map.of("plan_id", "1", "database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
+ Map<String, Object> expectedPlanArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_plan_mask_rule", rawPlanArguments, "plan-response"));
when(getMCPInteractionClient().call("database_gateway_plan_mask_rule", expectedPlanArguments)).thenReturn(Map.of("plan_id", "plan-1"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
- assertThat(actual.getInteractionTrace().get(0).getArguments(), CoreMatchers.is(expectedPlanArguments));
- assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
+ assertThat(actual.getInteractionTrace().get(0).getArguments(), is(expectedPlanArguments));
+ assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
verify(getMCPInteractionClient()).call("database_gateway_plan_mask_rule", expectedPlanArguments);
}
@Test
void assertRunKeepsCurrentPlanningPlanId() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_plan_mask_rule"));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final Map<String, Object> initialPlanArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
- final Map<String, Object> currentPlanArguments = Map.of("plan_id", "plan-1", "database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_plan_mask_rule"));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ Map<String, Object> initialPlanArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
+ Map<String, Object> currentPlanArguments = Map.of("plan_id", "plan-1", "database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_plan_mask_rule", initialPlanArguments, "initial-plan-response"));
when(getLLMChatClient().complete(anyList(), anyList(), eq("auto"), eq(false))).thenReturn(
@@ -205,19 +206,19 @@ class LLMMCPConversationRunnerCoverageTest extends AbstractLLMMCPConversationRun
"arguments", currentPlanArguments))));
when(getMCPInteractionClient().call("database_gateway_plan_mask_rule", currentPlanArguments)).thenReturn(Map.of("plan_id", "plan-1"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
- assertThat(actual.getInteractionTrace().get(1).getArguments(), CoreMatchers.is(currentPlanArguments));
- assertThat(actual.getInteractionTrace().get(1).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
+ assertThat(actual.getInteractionTrace().get(1).getArguments(), is(currentPlanArguments));
+ assertThat(actual.getInteractionTrace().get(1).getActionOrigin(), is(MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN));
verify(getMCPInteractionClient()).call("database_gateway_plan_mask_rule", currentPlanArguments);
}
@Test
void assertRunRequiresToolCallWhenExpectedExecuteQueryIsReturnedAsText() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion(QUERY, List.of(), "query-text-response"),
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "query-tool-response"));
@@ -225,35 +226,35 @@ class LLMMCPConversationRunnerCoverageTest extends AbstractLLMMCPConversationRun
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of("database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(1));
- assertThat(actual.getInteractionTrace().get(0).getTargetName(), CoreMatchers.is("database_gateway_execute_query"));
- assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN));
+ assertThat(actual.getInteractionTrace().size(), is(1));
+ assertThat(actual.getInteractionTrace().get(0).getTargetName(), is("database_gateway_execute_query"));
+ assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), is(MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN));
verify(getMCPInteractionClient()).call("database_gateway_execute_query", executeQueryArguments);
verify(getLLMChatClient(), times(2)).complete(anyList(), anyList(), eq("required"), eq(false));
}
@Test
void assertRunNamesLatestPlanIdWhenWorkflowCoverageIsMissing() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_plan_mask_rule", "database_gateway_apply_workflow"));
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> planArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_plan_mask_rule", "database_gateway_apply_workflow"));
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> planArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_plan_mask_rule", planArguments, "plan-response"),
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response-1"));
when(getMCPInteractionClient().call("database_gateway_plan_mask_rule", planArguments)).thenReturn(Map.of("plan_id", "plan-1"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
verify(getLLMChatClient(), times(3)).complete(actualMessages.capture(), anyList(), eq("required"), eq(false));
- final List<LLMChatMessage> actualThirdTurnMessages = actualMessages.getAllValues().get(2);
- final String actualRetryInstruction = actualThirdTurnMessages.get(actualThirdTurnMessages.size() - 1).getContent();
- assertThat(actualRetryInstruction, CoreMatchers.containsString("Remaining required MCP tools: database_gateway_apply_workflow"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("set plan_id `plan-1`"));
- assertThat(actualRetryInstruction, CoreMatchers.containsString("do not use placeholder text `plan_id`"));
+ List<LLMChatMessage> actualThirdTurnMessages = actualMessages.getAllValues().get(2);
+ String actualRetryInstruction = actualThirdTurnMessages.get(actualThirdTurnMessages.size() - 1).getContent();
+ assertThat(actualRetryInstruction, containsString("Remaining required MCP tools: database_gateway_apply_workflow"));
+ assertThat(actualRetryInstruction, containsString("set plan_id `plan-1`"));
+ assertThat(actualRetryInstruction, containsString("do not use placeholder text `plan_id`"));
}
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerFailureTest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerFailureTest.java
index 55fac28a895..cf4e657e3e5 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerFailureTest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerFailureTest.java
@@ -23,7 +23,7 @@ import org.apache.shardingsphere.test.e2e.mcp.llm.conversation.client.LLMChatCom
import org.apache.shardingsphere.test.e2e.mcp.llm.conversation.client.LLMToolCall;
import org.apache.shardingsphere.test.e2e.mcp.llm.scenario.LLME2EScenario;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionActionNames;
-import org.hamcrest.CoreMatchers;
+import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
@@ -53,25 +53,25 @@ class LLMMCPConversationRunnerFailureTest extends AbstractLLMMCPConversationRunn
@Test
void assertRunWithExecuteQueryErrorPayloadIgnoredForCoverage() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"),
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response"));
when(getMCPInteractionClient().call("database_gateway_execute_query", executeQueryArguments)).thenReturn(Map.of("error_code", "tool_failed"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
verify(getLLMChatClient(), times(2)).complete(anyList(), anyList(), eq("required"), eq(false));
}
@Test
void assertRunWithInvalidFinalJson() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"));
when(getMCPInteractionClient().call("database_gateway_execute_query", executeQueryArguments)).thenReturn(createResultSetPayload(2));
@@ -79,63 +79,63 @@ class LLMMCPConversationRunnerFailureTest extends AbstractLLMMCPConversationRunn
new LLMChatCompletion("not-json", List.of(), "invalid-final-response-1"),
new LLMChatCompletion("still-not-json", List.of(), "invalid-final-response-2"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("invalid_final_json"));
- assertThat(actual.getRawModelOutputs(), CoreMatchers.is(List.of("tool-call-response", "invalid-final-response-1", "invalid-final-response-2")));
+ assertThat(actual.getAssertionReport().getFailureType(), is("invalid_final_json"));
+ assertThat(actual.getRawModelOutputs(), is(List.of("tool-call-response", "invalid-final-response-1", "invalid-final-response-2")));
verify(getMCPInteractionClient()).open();
verify(getMCPInteractionClient()).close();
}
@Test
void assertRunWithMcpActionFailure() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"));
when(getMCPInteractionClient().call("database_gateway_execute_query", executeQueryArguments)).thenThrow(new IOException("boom"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("mcp_runtime_unavailable"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("mcp_runtime_unavailable"));
}
@Test
void assertRunWithMcpRuntimeUnavailable() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
doThrow(new IOException("boom")).when(getMCPInteractionClient()).open();
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("mcp_runtime_unavailable"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("mcp_runtime_unavailable"));
verify(getMCPInteractionClient()).open();
verify(getMCPInteractionClient()).close();
}
@Test
void assertRunWithModelRequestIOException() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenThrow(new IOException("http 500"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("model_service_unavailable"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("model_service_unavailable"));
verify(getMCPInteractionClient()).open();
verify(getMCPInteractionClient()).close();
}
@Test
void assertRunWithInterruptedConversation() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenThrow(new InterruptedException("boom"));
try {
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("model_service_unavailable"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("model_service_unavailable"));
assertTrue(Thread.currentThread().isInterrupted());
} finally {
Thread.interrupted();
@@ -144,42 +144,42 @@ class LLMMCPConversationRunnerFailureTest extends AbstractLLMMCPConversationRunn
@Test
void assertRunWithModelServiceUnavailable() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
doThrow(new IllegalStateException("Model service is not ready for `ggml-org/Qwen3-1.7B-GGUF:Q4_K_M`.")).when(getLLMChatClient()).waitUntilReady();
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("model_service_unavailable"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("model_service_unavailable"));
verify(getMCPInteractionClient(), never()).open();
verify(getMCPInteractionClient()).close();
}
@Test
void assertRunIgnoresCloseIOException() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response"));
doThrow(new IOException("close failed")).when(getMCPInteractionClient()).close();
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
verify(getMCPInteractionClient()).close();
}
@Test
void assertRunRestoresInterruptOnCloseFailure() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response"));
doThrow(new InterruptedException("close interrupted")).when(getMCPInteractionClient()).close();
try {
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
assertTrue(Thread.currentThread().isInterrupted());
} finally {
Thread.interrupted();
@@ -188,15 +188,15 @@ class LLMMCPConversationRunnerFailureTest extends AbstractLLMMCPConversationRunn
@Test
void assertRunWithUnexpectedTool() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("", List.of(new LLMToolCall("tool-1", "unsupported_tool", "{}")), "tool-call-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("unexpected_tool_requested"));
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(1));
+ assertThat(actual.getAssertionReport().getFailureType(), is("unexpected_tool_requested"));
+ assertThat(actual.getInteractionTrace().size(), is(1));
verify(getMCPInteractionClient()).open();
verify(getMCPInteractionClient(), never()).call(anyString(), anyMap());
verify(getMCPInteractionClient()).close();
@@ -204,15 +204,15 @@ class LLMMCPConversationRunnerFailureTest extends AbstractLLMMCPConversationRunn
@Test
void assertRunWithInvalidToolArgumentsJson() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("", List.of(new LLMToolCall("tool-1", "database_gateway_execute_query", "{invalid")), "tool-call-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("invalid_tool_arguments"));
- assertThat(actual.getInteractionTrace().get(0).getTargetName(), CoreMatchers.is("database_gateway_execute_query"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("invalid_tool_arguments"));
+ assertThat(actual.getInteractionTrace().get(0).getTargetName(), is("database_gateway_execute_query"));
verify(getMCPInteractionClient()).open();
verify(getMCPInteractionClient(), never()).call(anyString(), anyMap());
verify(getMCPInteractionClient()).close();
@@ -220,79 +220,79 @@ class LLMMCPConversationRunnerFailureTest extends AbstractLLMMCPConversationRunn
@Test
void assertRunWithEmptyResourceUri() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.READ_RESOURCE));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.READ_RESOURCE));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", MCPInteractionActionNames.READ_RESOURCE, Map.of("uri", " "), "tool-call-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("invalid_tool_arguments"));
- assertThat(actual.getInteractionTrace().get(0).getActionKind(), CoreMatchers.is(MCPInteractionActionNames.RESOURCE_READ_KIND));
+ assertThat(actual.getAssertionReport().getFailureType(), is("invalid_tool_arguments"));
+ assertThat(actual.getInteractionTrace().get(0).getActionKind(), is(MCPInteractionActionNames.RESOURCE_READ_KIND));
verify(getMCPInteractionClient(), never()).readResource(anyString());
}
@Test
void assertRunWithUnsafeSqlAttempted() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query",
Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "sql", "UPDATE orders SET status = 'DONE'"),
"tool-call-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("unsafe_sql_attempted"));
- assertThat(actual.getInteractionTrace().get(0).getTargetName(), CoreMatchers.is("database_gateway_execute_query"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("unsafe_sql_attempted"));
+ assertThat(actual.getInteractionTrace().get(0).getTargetName(), is("database_gateway_execute_query"));
verify(getMCPInteractionClient(), never()).call(anyString(), anyMap());
}
@Test
void assertRunWithUnsafeExecuteUpdateAttempted() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_update"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_update"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_update",
Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "sql", "UPDATE orders SET status = 'DONE'", "execution_mode", "execute"),
"tool-call-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("unsafe_sql_execution_attempted"));
- assertThat(actual.getInteractionTrace().get(0).getTargetName(), CoreMatchers.is("database_gateway_execute_update"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("unsafe_sql_execution_attempted"));
+ assertThat(actual.getInteractionTrace().get(0).getTargetName(), is("database_gateway_execute_update"));
verify(getMCPInteractionClient(), never()).call(anyString(), anyMap());
}
@Test
void assertRunWithUnsafeWorkflowExecutionAttempted() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_apply_workflow"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_apply_workflow"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_apply_workflow", Map.of("plan_id", "plan-1", "execution_mode", "review-then-execute"), "tool-call-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("unsafe_workflow_execution_attempted"));
- assertThat(actual.getInteractionTrace().get(0).getTargetName(), CoreMatchers.is("database_gateway_apply_workflow"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("unsafe_workflow_execution_attempted"));
+ assertThat(actual.getInteractionTrace().get(0).getTargetName(), is("database_gateway_apply_workflow"));
verify(getMCPInteractionClient(), never()).call(anyString(), anyMap());
}
@ParameterizedTest(name = "{0}")
@MethodSource("unexpectedQueryResultCases")
void assertRunWithUnexpectedQueryResult(final String caseName, final Map<String, Object> finalAnswerPayload) throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"));
when(getMCPInteractionClient().call("database_gateway_execute_query", executeQueryArguments)).thenReturn(createResultSetPayload(2));
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
new LLMChatCompletion(JsonUtils.toJsonString(finalAnswerPayload), List.of(), caseName));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("unexpected_query_result"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("unexpected_query_result"));
verify(getMCPInteractionClient()).call("database_gateway_execute_query", executeQueryArguments);
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerNextActionTest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerNextActionTest.java
index b3c6df4263c..87725b31a14 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerNextActionTest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerNextActionTest.java
@@ -25,7 +25,7 @@ import org.apache.shardingsphere.test.e2e.mcp.llm.conversation.client.LLMToolCal
import org.apache.shardingsphere.test.e2e.mcp.llm.scenario.LLME2EScenario;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionActionNames;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionTraceRecord;
-import org.hamcrest.CoreMatchers;
+import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
@@ -51,10 +51,10 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
@Test
void assertRunRetriesExpectedExecuteQueryBeforeFinalAnswer() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(4);
- final Map<String, Object> wrongQueryArguments = Map.of("database", "analytics_db", "schema", SCHEMA_NAME, "sql", QUERY, "max_rows", 10);
- final Map<String, Object> expectedQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(4);
+ Map<String, Object> wrongQueryArguments = Map.of("database", "analytics_db", "schema", SCHEMA_NAME, "sql", QUERY, "max_rows", 10);
+ Map<String, Object> expectedQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", wrongQueryArguments, "wrong-query-response"));
@@ -65,23 +65,23 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of("database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient()).complete(actualMessages.capture(), anyList(), eq("auto"), eq(false));
- final List<LLMChatMessage> actualAutoTurnMessages = actualMessages.getValue();
+ List<LLMChatMessage> actualAutoTurnMessages = actualMessages.getValue();
assertTrue(actualAutoTurnMessages.stream()
.map(LLMChatMessage::getContent)
.anyMatch(each -> each.contains("latest successful database_gateway_execute_query did not use database `logic_db`")));
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(2));
+ assertThat(actual.getInteractionTrace().size(), is(2));
}
@Test
void assertRunRecoversCompletionArguments() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.COMPLETE, "database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(4);
- final Map<String, Object> completionReference = Map.of("type", "ref/prompt", "name", PROMPT_NAME);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.COMPLETE, "database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(4);
+ Map<String, Object> completionReference = Map.of("type", "ref/prompt", "name", PROMPT_NAME);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", MCPInteractionActionNames.COMPLETE, Map.of("argument_name", "schema", "argument_value", "pub"), "bad-completion-response"),
new LLMChatCompletion("",
@@ -97,11 +97,11 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of(MCPInteractionActionNames.COMPLETE, "database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(3));
- assertThat(actual.getInteractionTrace().get(0).getStructuredContent().get("error_code"), CoreMatchers.is("invalid_tool_arguments"));
+ assertThat(actual.getInteractionTrace().size(), is(3));
+ assertThat(actual.getInteractionTrace().get(0).getStructuredContent().get("error_code"), is("invalid_tool_arguments"));
verify(getMCPInteractionClient()).complete(completionReference, "schema", "pub", Map.of());
}
@@ -109,14 +109,14 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
void assertRunNormalizesCompletionArgumentName() throws IOException, InterruptedException {
final LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.COMPLETE, "database_gateway_execute_query"));
final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> completionReference = Map.of("type", "ref/prompt", "name", PROMPT_NAME);
- final Map<String, String> completionContext = Map.of("database", DATABASE_NAME);
- final Map<String, Object> completionArguments = new LinkedHashMap<>(4, 1F);
+ Map<String, Object> completionReference = Map.of("type", "ref/prompt", "name", PROMPT_NAME);
+ Map<String, String> completionContext = Map.of("database", DATABASE_NAME);
+ Map<String, Object> completionArguments = new LinkedHashMap<>(4, 1F);
completionArguments.put("reference", completionReference);
completionArguments.put("reference argument_name", "schema");
completionArguments.put("argument_value", "pub");
completionArguments.put("context_arguments", completionContext);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", MCPInteractionActionNames.COMPLETE, completionArguments, "completion-response"),
createToolCallCompletion("tool-2", "database_gateway_execute_query", executeQueryArguments, "query-response"));
@@ -125,21 +125,21 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of(MCPInteractionActionNames.COMPLETE, "database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(2));
+ assertThat(actual.getInteractionTrace().size(), is(2));
verify(getMCPInteractionClient()).complete(completionReference, "schema", "pub", completionContext);
}
@Test
void assertRunDefaultsCompletionReferenceFromPrompt() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.GET_PROMPT, MCPInteractionActionNames.COMPLETE, "database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(5);
- final Map<String, Object> getPromptArguments = Map.of("name", PROMPT_NAME);
- final Map<String, Object> completionReference = Map.of("type", "ref/prompt", "name", PROMPT_NAME);
- final Map<String, Object> completionArguments = Map.of("argument_name", "schema", "argument_value", "pub");
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of(MCPInteractionActionNames.GET_PROMPT, MCPInteractionActionNames.COMPLETE, "database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(5);
+ Map<String, Object> getPromptArguments = Map.of("name", PROMPT_NAME);
+ Map<String, Object> completionReference = Map.of("type", "ref/prompt", "name", PROMPT_NAME);
+ Map<String, Object> completionArguments = Map.of("argument_name", "schema", "argument_value", "pub");
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", MCPInteractionActionNames.GET_PROMPT, getPromptArguments, "prompt-response"),
@@ -153,23 +153,23 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of(MCPInteractionActionNames.GET_PROMPT, MCPInteractionActionNames.COMPLETE, "database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient(), times(3)).complete(anyList(), actualTools.capture(), eq("required"), eq(false));
- assertThat(getToolNames(actualTools.getAllValues().get(1)), CoreMatchers.is(List.of(MCPInteractionActionNames.COMPLETE)));
- assertThat(getToolNames(actualTools.getAllValues().get(2)), CoreMatchers.is(List.of("database_gateway_execute_query")));
+ assertThat(getToolNames(actualTools.getAllValues().get(1)), is(List.of(MCPInteractionActionNames.COMPLETE)));
+ assertThat(getToolNames(actualTools.getAllValues().get(2)), is(List.of("database_gateway_execute_query")));
verify(getMCPInteractionClient()).complete(completionReference, "schema", "pub", Map.of());
}
@Test
void assertRunPromptsImmediateResourceNextAction() throws IOException, InterruptedException {
- final List<String> toolNames = List.of(MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(4);
- final Map<String, Object> runtimeArguments = Map.of("uri", "shardingsphere://runtime");
- final Map<String, Object> capabilitiesArguments = Map.of("uri", RESOURCE_URI);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ List<String> toolNames = List.of(MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(4);
+ Map<String, Object> runtimeArguments = Map.of("uri", "shardingsphere://runtime");
+ Map<String, Object> capabilitiesArguments = Map.of("uri", RESOURCE_URI);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
final ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
@@ -184,22 +184,22 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of(MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient(), times(3)).complete(actualMessages.capture(), actualTools.capture(), eq("required"), eq(false));
assertTrue(containsMessage(actualMessages.getAllValues().get(1), "Call mcp_read_resource with uri `" + RESOURCE_URI + "` now"));
- assertThat(getToolNames(actualTools.getAllValues().get(1)), CoreMatchers.is(List.of(MCPInteractionActionNames.READ_RESOURCE)));
+ assertThat(getToolNames(actualTools.getAllValues().get(1)), is(List.of(MCPInteractionActionNames.READ_RESOURCE)));
}
@Test
void assertRunPromptsImmediateToolNextAction() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_plan_mask_rule", "database_gateway_apply_workflow", "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(4);
- final Map<String, Object> planArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
- final Map<String, Object> applyArguments = Map.of("plan_id", "plan-1", "execution_mode", "preview");
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ List<String> toolNames = List.of("database_gateway_plan_mask_rule", "database_gateway_apply_workflow", "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(4);
+ Map<String, Object> planArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status");
+ Map<String, Object> applyArguments = Map.of("plan_id", "plan-1", "execution_mode", "preview");
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
final ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
@@ -215,24 +215,24 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().get(1).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
+ assertThat(actual.getInteractionTrace().get(1).getActionOrigin(), is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
verify(getLLMChatClient(), times(3)).complete(actualMessages.capture(), actualTools.capture(), eq("required"), eq(false));
assertTrue(containsMessage(actualMessages.getAllValues().get(1), "Call `database_gateway_apply_workflow` now with exactly these arguments"));
assertTrue(containsMessage(actualMessages.getAllValues().get(1), "\"plan_id\":\"plan-1\""));
- assertThat(getToolNames(actualTools.getAllValues().get(1)), CoreMatchers.is(List.of("database_gateway_apply_workflow")));
+ assertThat(getToolNames(actualTools.getAllValues().get(1)), is(List.of("database_gateway_apply_workflow")));
}
@Test
void assertRunPromptsExactResourceAfterList() throws IOException, InterruptedException {
- final List<String> toolNames = List.of(MCPInteractionActionNames.LIST_RESOURCES, MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
- final String tableResourceUri = "shardingsphere://databases/logic_db/schemas/public/tables/orders";
- final LLME2EScenario actualScenario = createScenario(toolNames, "Read exact `" + tableResourceUri + "` before querying.");
- final LLMMCPConversationRunner actualRunner = createRunner(4);
- final Map<String, Object> readResourceArguments = Map.of("uri", "{description=Resource URI to read., type=string}");
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ List<String> toolNames = List.of(MCPInteractionActionNames.LIST_RESOURCES, MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
+ String tableResourceUri = "shardingsphere://databases/logic_db/schemas/public/tables/orders";
+ LLME2EScenario actualScenario = createScenario(toolNames, "Read exact `" + tableResourceUri + "` before querying.");
+ LLMMCPConversationRunner actualRunner = createRunner(4);
+ Map<String, Object> readResourceArguments = Map.of("uri", "{description=Resource URI to read., type=string}");
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", MCPInteractionActionNames.LIST_RESOURCES, Map.of(), "list-response"),
@@ -244,7 +244,7 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient(), times(3)).complete(actualMessages.capture(), anyList(), eq("required"), eq(false));
@@ -254,15 +254,15 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
@Test
void assertRunPromptsLiveResourceAfterStaleResourceMiss() throws IOException, InterruptedException {
- final List<String> toolNames = List.of(MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
- final String staleResourceUri = "shardingsphere://databases/unknown/schemas/unknown/tables/orders";
- final String tableResourceUri = "shardingsphere://databases/logic_db/schemas/public/tables/orders";
- final LLME2EScenario actualScenario = createScenario(toolNames,
+ List<String> toolNames = List.of(MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
+ String staleResourceUri = "shardingsphere://databases/unknown/schemas/unknown/tables/orders";
+ String tableResourceUri = "shardingsphere://databases/logic_db/schemas/public/tables/orders";
+ LLME2EScenario actualScenario = createScenario(toolNames,
"Read stale `" + staleResourceUri + "`, recover by reading exact live table resource `" + tableResourceUri + "`, then query.");
- final LLMMCPConversationRunner actualRunner = createRunner(4);
- final Map<String, Object> staleReadArguments = Map.of("uri", staleResourceUri);
- final Map<String, Object> liveReadArguments = Map.of("uri", tableResourceUri);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLMMCPConversationRunner actualRunner = createRunner(4);
+ Map<String, Object> staleReadArguments = Map.of("uri", staleResourceUri);
+ Map<String, Object> liveReadArguments = Map.of("uri", tableResourceUri);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", MCPInteractionActionNames.READ_RESOURCE, staleReadArguments, "stale-response"),
@@ -278,22 +278,22 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient(), times(3)).complete(actualMessages.capture(), anyList(), eq("required"), eq(false));
assertTrue(containsMessage(actualMessages.getAllValues().get(1), "Use exactly `" + tableResourceUri + "` as uri"));
- assertThat(actual.getInteractionTrace().get(1).getArguments().get("uri"), CoreMatchers.is(tableResourceUri));
+ assertThat(actual.getInteractionTrace().get(1).getArguments().get("uri"), is(tableResourceUri));
}
@Test
void assertRunFollowsPendingNextActionBeforeFinalAnswer() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_apply_workflow", "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
- final Map<String, Object> previewArguments = Map.of("plan_id", "plan-1", "execution_mode", "preview");
- final Map<String, Object> manualArguments = Map.of("plan_id", "plan-1", "execution_mode", "manual-only");
+ List<String> toolNames = List.of("database_gateway_apply_workflow", "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ Map<String, Object> previewArguments = Map.of("plan_id", "plan-1", "execution_mode", "preview");
+ Map<String, Object> manualArguments = Map.of("plan_id", "plan-1", "execution_mode", "manual-only");
final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(new LLMChatCompletion("",
List.of(
@@ -311,7 +311,7 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of("database_gateway_execute_query", "database_gateway_apply_workflow"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient()).complete(actualMessages.capture(), anyList(), eq("auto"), eq(false));
@@ -320,16 +320,16 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
@Test
void assertRunIgnoresSideEffectExecutionNextActionBeforeReadOnlyVerification() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_execute_update", "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> previewArguments = Map.of(
+ List<String> toolNames = List.of("database_gateway_execute_update", "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> previewArguments = Map.of(
"database", DATABASE_NAME,
"schema", SCHEMA_NAME,
"sql", "UPDATE orders SET status = status WHERE order_id = -1",
"execution_mode", "preview");
- final Map<String, Object> executeQueryArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "sql", QUERY);
- final Map<String, Object> misroutedQueryArguments = Map.of(
+ Map<String, Object> executeQueryArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "sql", QUERY);
+ Map<String, Object> misroutedQueryArguments = Map.of(
"database", DATABASE_NAME,
"schema", SCHEMA_NAME,
"sql", QUERY,
@@ -347,30 +347,30 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient(), times(2)).complete(actualMessages.capture(), actualTools.capture(), eq("required"), eq(false));
- final List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
+ List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
assertTrue(containsMessage(actualSecondTurnMessages, "side-effect execution next_actions"));
- assertThat(getToolNames(actualTools.getAllValues().get(1)), CoreMatchers.is(List.of("database_gateway_execute_query")));
- assertThat(actual.getInteractionTrace().get(1).getTargetName(), CoreMatchers.is("database_gateway_execute_query"));
- assertThat(actual.getInteractionTrace().get(1).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
+ assertThat(getToolNames(actualTools.getAllValues().get(1)), is(List.of("database_gateway_execute_query")));
+ assertThat(actual.getInteractionTrace().get(1).getTargetName(), is("database_gateway_execute_query"));
+ assertThat(actual.getInteractionTrace().get(1).getActionOrigin(), is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
verify(getMCPInteractionClient(), never()).call("database_gateway_execute_update", misroutedQueryArguments);
}
@Test
void assertRunRoutesReadOnlyCallAfterSideEffectPreviewInSameCompletion() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_execute_update", "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> previewArguments = Map.of(
+ List<String> toolNames = List.of("database_gateway_execute_update", "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> previewArguments = Map.of(
"database", DATABASE_NAME,
"schema", SCHEMA_NAME,
"sql", "UPDATE orders SET status = status WHERE order_id = -1",
"execution_mode", "preview");
- final Map<String, Object> executeQueryArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "sql", QUERY);
- final Map<String, Object> misroutedQueryArguments = Map.of(
+ Map<String, Object> executeQueryArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "sql", QUERY);
+ Map<String, Object> misroutedQueryArguments = Map.of(
"database", DATABASE_NAME,
"schema", SCHEMA_NAME,
"sql", QUERY,
@@ -388,22 +388,22 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().get(0).getTargetName(), CoreMatchers.is("database_gateway_execute_update"));
- assertThat(actual.getInteractionTrace().get(1).getTargetName(), CoreMatchers.is("database_gateway_execute_query"));
- assertThat(actual.getInteractionTrace().get(1).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
+ assertThat(actual.getInteractionTrace().get(0).getTargetName(), is("database_gateway_execute_update"));
+ assertThat(actual.getInteractionTrace().get(1).getTargetName(), is("database_gateway_execute_query"));
+ assertThat(actual.getInteractionTrace().get(1).getActionOrigin(), is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
verify(getMCPInteractionClient(), never()).call("database_gateway_execute_update", misroutedQueryArguments);
}
@Test
void assertRunCompactsManualArtifactsBeforeReadOnlyVerification() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_apply_workflow", "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> manualArguments = Map.of("plan_id", "plan-1", "execution_mode", "manual-only");
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ List<String> toolNames = List.of("database_gateway_apply_workflow", "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> manualArguments = Map.of("plan_id", "plan-1", "execution_mode", "manual-only");
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<LLMChatMessage>> actualMessages = createChatMessagesCaptor();
final ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
@@ -420,13 +420,13 @@ class LLMMCPConversationRunnerNextActionTest extends AbstractLLMMCPConversationR
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient(), times(2)).complete(actualMessages.capture(), actualTools.capture(), eq("required"), eq(false));
- final List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
+ List<LLMChatMessage> actualSecondTurnMessages = actualMessages.getAllValues().get(1);
assertTrue(containsMessage(actualSecondTurnMessages, "\"distsql_artifact_count\":1"));
assertFalse(actualSecondTurnMessages.stream().map(LLMChatMessage::getContent).anyMatch(each -> each.contains("CREATE MASK RULE orders SECRET")));
- assertThat(getToolNames(actualTools.getAllValues().get(1)), CoreMatchers.is(List.of("database_gateway_execute_query")));
+ assertThat(getToolNames(actualTools.getAllValues().get(1)), is(List.of("database_gateway_execute_query")));
}
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerTest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerTest.java
index 657f5e08aac..f57e4d60e98 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerTest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPConversationRunnerTest.java
@@ -25,7 +25,9 @@ import org.apache.shardingsphere.test.e2e.mcp.llm.conversation.client.LLMToolCal
import org.apache.shardingsphere.test.e2e.mcp.llm.scenario.LLME2EScenario;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionActionNames;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionTraceRecord;
-import org.hamcrest.CoreMatchers;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
@@ -49,9 +51,9 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
@Test
void assertRunWithExecuteQuery() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"));
@@ -59,53 +61,53 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of("database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(1));
- assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN));
- assertThat(actual.getRawModelOutputs(), CoreMatchers.is(List.of("tool-call-response", "final-answer-response")));
+ assertThat(actual.getInteractionTrace().size(), is(1));
+ assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), is(MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN));
+ assertThat(actual.getRawModelOutputs(), is(List.of("tool-call-response", "final-answer-response")));
verify(getLLMChatClient()).complete(anyList(), actualTools.capture(), eq("required"), eq(false));
verify(getMCPInteractionClient()).open();
verify(getMCPInteractionClient()).call("database_gateway_execute_query", executeQueryArguments);
verify(getMCPInteractionClient()).close();
- assertThat(getToolName(actualTools.getValue().get(0)), CoreMatchers.is("database_gateway_execute_query"));
- assertThat(getMap(getFunction(actualTools.getValue().get(0)).get("parameters")).get("required"), CoreMatchers.is(List.of("database", "sql")));
+ assertThat(getToolName(actualTools.getValue().get(0)), is("database_gateway_execute_query"));
+ assertThat(getMap(getFunction(actualTools.getValue().get(0)).get("parameters")).get("required"), is(List.of("database", "sql")));
}
@Test
void assertRunNormalizesMissingExecuteQuerySchema() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> modelQueryArguments = Map.of("database", DATABASE_NAME, "sql", QUERY, "max_rows", 10);
- final Map<String, Object> expectedQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> modelQueryArguments = Map.of("database", DATABASE_NAME, "sql", QUERY, "max_rows", 10);
+ Map<String, Object> expectedQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", modelQueryArguments, "tool-call-response"));
when(getMCPInteractionClient().call("database_gateway_execute_query", expectedQueryArguments)).thenReturn(createResultSetPayload(2));
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of("database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
- assertThat(actual.getInteractionTrace().get(0).getArguments(), CoreMatchers.is(expectedQueryArguments));
+ assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
+ assertThat(actual.getInteractionTrace().get(0).getArguments(), is(expectedQueryArguments));
verify(getMCPInteractionClient()).call("database_gateway_execute_query", expectedQueryArguments);
verify(getLLMChatClient(), never()).complete(anyList(), anyList(), eq("auto"), eq(false));
}
@Test
void assertRunNormalizesMissingSearchMetadataScope() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_search_metadata", "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames, "Use logical database `logic_db` and schema `public` when searching.");
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> modelSearchArguments = Map.of("query", TABLE_NAME, "object_types", List.of("table"));
- final Map<String, Object> expectedSearchArguments = Map.of(
+ List<String> toolNames = List.of("database_gateway_search_metadata", "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames, "Use logical database `logic_db` and schema `public` when searching.");
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> modelSearchArguments = Map.of("query", TABLE_NAME, "object_types", List.of("table"));
+ Map<String, Object> expectedSearchArguments = Map.of(
"query", TABLE_NAME,
"object_types", List.of("table"),
"database", DATABASE_NAME,
"schema", SCHEMA_NAME);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(new LLMChatCompletion("",
List.of(
new LLMToolCall("tool-1", "database_gateway_search_metadata", JsonUtils.toJsonString(modelSearchArguments)),
@@ -117,41 +119,41 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
- assertThat(actual.getInteractionTrace().get(0).getArguments(), CoreMatchers.is(expectedSearchArguments));
+ assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), is(MCPInteractionTraceRecord.HARNESS_ARGUMENT_NORMALIZATION_ORIGIN));
+ assertThat(actual.getInteractionTrace().get(0).getArguments(), is(expectedSearchArguments));
verify(getMCPInteractionClient()).call("database_gateway_search_metadata", expectedSearchArguments);
}
@Test
void assertRunNormalizesSchemaQualifiedExecuteQuerySchema() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final String actualQuery = "SELECT COUNT(*) AS total_orders FROM public.orders";
- final Map<String, Object> modelQueryArguments = Map.of("database", DATABASE_NAME, "sql", actualQuery);
- final Map<String, Object> expectedQueryArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "sql", actualQuery);
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ String actualQuery = "SELECT COUNT(*) AS total_orders FROM public.orders";
+ Map<String, Object> modelQueryArguments = Map.of("database", DATABASE_NAME, "sql", actualQuery);
+ Map<String, Object> expectedQueryArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "sql", actualQuery);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", modelQueryArguments, "tool-call-response"));
when(getMCPInteractionClient().call("database_gateway_execute_query", expectedQueryArguments)).thenReturn(createResultSetPayload(2));
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(List.of("database_gateway_execute_query"), 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().get(0).getArguments(), CoreMatchers.is(expectedQueryArguments));
+ assertThat(actual.getInteractionTrace().get(0).getArguments(), is(expectedQueryArguments));
verify(getLLMChatClient(), never()).complete(anyList(), anyList(), eq("auto"), eq(false));
}
@Test
void assertRunPromptsExactFinalInteractionSequence() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_execute_query", "database_gateway_execute_update");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
- final Map<String, Object> executeUpdateArguments = Map.of(
+ List<String> toolNames = List.of("database_gateway_execute_query", "database_gateway_execute_update");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ Map<String, Object> executeUpdateArguments = Map.of(
"database", DATABASE_NAME,
"schema", SCHEMA_NAME,
"sql", "UPDATE orders SET status = status WHERE order_id = -1",
@@ -167,22 +169,22 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient()).complete(actualFinalMessages.capture(), eq(List.of()), eq("none"), eq(true));
- final List<LLMChatMessage> actualMessages = actualFinalMessages.getValue();
- final String actualInstruction = actualMessages.get(actualMessages.size() - 1).getContent();
- assertThat(actualInstruction, CoreMatchers.containsString("interactionSequence exactly to this JSON array: [\"database_gateway_execute_query\",\"database_gateway_execute_update\"]"));
- assertThat(actualInstruction, CoreMatchers.not(CoreMatchers.containsString("database_gateway_search_metadata")));
+ List<LLMChatMessage> actualMessages = actualFinalMessages.getValue();
+ String actualInstruction = actualMessages.get(actualMessages.size() - 1).getContent();
+ assertThat(actualInstruction, containsString("interactionSequence exactly to this JSON array: [\"database_gateway_execute_query\",\"database_gateway_execute_update\"]"));
+ assertThat(actualInstruction, not(containsString("database_gateway_search_metadata")));
}
@Test
void assertRunWithResourceBridgeSequence() throws IOException, InterruptedException {
- final List<String> actualToolNames = List.of(MCPInteractionActionNames.LIST_RESOURCES, MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(actualToolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ List<String> actualToolNames = List.of(MCPInteractionActionNames.LIST_RESOURCES, MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(actualToolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(new LLMChatCompletion("",
List.of(
new LLMToolCall("tool-1", MCPInteractionActionNames.LIST_RESOURCES, "{}"),
@@ -195,15 +197,15 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(actualToolNames, "2", "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(3));
- assertThat(actual.getInteractionTrace().get(0).getActionKind(), CoreMatchers.is(MCPInteractionActionNames.RESOURCE_LIST_KIND));
- assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.PROTOCOL_BRIDGE_ORIGIN));
- assertThat(actual.getInteractionTrace().get(1).getActionKind(), CoreMatchers.is(MCPInteractionActionNames.RESOURCE_READ_KIND));
- assertThat(actual.getInteractionTrace().get(2).getTargetName(), CoreMatchers.is("database_gateway_execute_query"));
- assertThat(actual.getInteractionTrace().get(2).getActionOrigin(), CoreMatchers.is(MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN));
+ assertThat(actual.getInteractionTrace().size(), is(3));
+ assertThat(actual.getInteractionTrace().get(0).getActionKind(), is(MCPInteractionActionNames.RESOURCE_LIST_KIND));
+ assertThat(actual.getInteractionTrace().get(0).getActionOrigin(), is(MCPInteractionTraceRecord.PROTOCOL_BRIDGE_ORIGIN));
+ assertThat(actual.getInteractionTrace().get(1).getActionKind(), is(MCPInteractionActionNames.RESOURCE_READ_KIND));
+ assertThat(actual.getInteractionTrace().get(2).getTargetName(), is("database_gateway_execute_query"));
+ assertThat(actual.getInteractionTrace().get(2).getActionOrigin(), is(MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN));
verify(getMCPInteractionClient()).listResources();
verify(getMCPInteractionClient()).readResource(RESOURCE_URI);
verify(getMCPInteractionClient()).call("database_gateway_execute_query", executeQueryArguments);
@@ -211,15 +213,15 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
@Test
void assertRunWithWorkflowContextRecoveryReadback() throws IOException, InterruptedException {
- final List<String> actualToolNames = List.of(MCPInteractionActionNames.READ_RESOURCE, "database_gateway_plan_mask_rule", "database_gateway_apply_workflow",
+ List<String> actualToolNames = List.of(MCPInteractionActionNames.READ_RESOURCE, "database_gateway_plan_mask_rule", "database_gateway_apply_workflow",
"database_gateway_validate_workflow", "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(actualToolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final String workflowResourceUri = "shardingsphere://workflows/plan-1";
- final Map<String, Object> planArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status", "algorithm_type", "MD5");
- final Map<String, Object> applyArguments = Map.of("plan_id", "plan-1", "execution_mode", "manual-only");
- final Map<String, Object> validateArguments = Map.of("plan_id", "plan-1");
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(actualToolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ String workflowResourceUri = "shardingsphere://workflows/plan-1";
+ Map<String, Object> planArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "table", TABLE_NAME, "column", "status", "algorithm_type", "MD5");
+ Map<String, Object> applyArguments = Map.of("plan_id", "plan-1", "execution_mode", "manual-only");
+ Map<String, Object> validateArguments = Map.of("plan_id", "plan-1");
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(new LLMChatCompletion("",
List.of(
new LLMToolCall("tool-1", MCPInteractionActionNames.READ_RESOURCE, JsonUtils.toJsonString(Map.of("uri", "shardingsphere://features/mask/algorithms"))),
@@ -244,10 +246,10 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
"database_gateway_validate_workflow", "database_gateway_execute_query"), 2,
"final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(7));
+ assertThat(actual.getInteractionTrace().size(), is(7));
verify(getMCPInteractionClient()).readResource(workflowResourceUri);
verify(getMCPInteractionClient()).call("database_gateway_apply_workflow", applyArguments);
verify(getMCPInteractionClient()).call("database_gateway_validate_workflow", validateArguments);
@@ -255,15 +257,15 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
@Test
void assertRunWithPromptAndCompletionBridgeSequence() throws IOException, InterruptedException {
- final List<String> actualToolNames = List.of(MCPInteractionActionNames.LIST_PROMPTS, MCPInteractionActionNames.GET_PROMPT, MCPInteractionActionNames.COMPLETE,
+ List<String> actualToolNames = List.of(MCPInteractionActionNames.LIST_PROMPTS, MCPInteractionActionNames.GET_PROMPT, MCPInteractionActionNames.COMPLETE,
"database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(actualToolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(3);
- final Map<String, Object> promptArguments = Map.of("query", "orders", "database", DATABASE_NAME);
- final Map<String, Object> completionReference = Map.of("type", "ref/prompt", "name", PROMPT_NAME);
- final Map<String, Object> completionReferenceArgument = Map.of("type", "prompt", "name", PROMPT_NAME);
- final Map<String, String> completionContext = Map.of("database", DATABASE_NAME);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ LLME2EScenario actualScenario = createScenario(actualToolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(3);
+ Map<String, Object> promptArguments = Map.of("query", "orders", "database", DATABASE_NAME);
+ Map<String, Object> completionReference = Map.of("type", "ref/prompt", "name", PROMPT_NAME);
+ Map<String, Object> completionReferenceArgument = Map.of("type", "prompt", "name", PROMPT_NAME);
+ Map<String, String> completionContext = Map.of("database", DATABASE_NAME);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(new LLMChatCompletion("",
List.of(
@@ -283,50 +285,50 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(actualToolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
- assertThat(actual.getInteractionTrace().size(), CoreMatchers.is(4));
- assertThat(actual.getInteractionTrace().get(0).getActionKind(), CoreMatchers.is(MCPInteractionActionNames.PROMPT_LIST_KIND));
- assertThat(actual.getInteractionTrace().get(1).getActionKind(), CoreMatchers.is(MCPInteractionActionNames.PROMPT_GET_KIND));
- assertThat(actual.getInteractionTrace().get(2).getActionKind(), CoreMatchers.is(MCPInteractionActionNames.COMPLETION_KIND));
+ assertThat(actual.getInteractionTrace().size(), is(4));
+ assertThat(actual.getInteractionTrace().get(0).getActionKind(), is(MCPInteractionActionNames.PROMPT_LIST_KIND));
+ assertThat(actual.getInteractionTrace().get(1).getActionKind(), is(MCPInteractionActionNames.PROMPT_GET_KIND));
+ assertThat(actual.getInteractionTrace().get(2).getActionKind(), is(MCPInteractionActionNames.COMPLETION_KIND));
verify(getLLMChatClient()).complete(anyList(), actualTools.capture(), eq("required"), eq(false));
verify(getMCPInteractionClient()).listPrompts();
verify(getMCPInteractionClient()).getPrompt(PROMPT_NAME, promptArguments);
verify(getMCPInteractionClient()).complete(completionReference, "schema", "pub", completionContext);
- assertThat(getToolName(actualTools.getValue().get(0)), CoreMatchers.is(MCPInteractionActionNames.LIST_PROMPTS));
+ assertThat(getToolName(actualTools.getValue().get(0)), is(MCPInteractionActionNames.LIST_PROMPTS));
}
@Test
void assertRunWithSearchMetadataToolDefinition() throws IOException, InterruptedException {
- final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_search_metadata"));
- final LLMMCPConversationRunner actualRunner = createRunner(1);
- final ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
+ LLME2EScenario actualScenario = createScenario(List.of("database_gateway_search_metadata"));
+ LLMMCPConversationRunner actualRunner = createRunner(1);
+ ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
new LLMChatCompletion("I already know the answer.", List.of(), "direct-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
- assertThat(actual.getAssertionReport().getFailureType(), CoreMatchers.is("missing_required_tool_coverage"));
+ assertThat(actual.getAssertionReport().getFailureType(), is("missing_required_tool_coverage"));
verify(getLLMChatClient()).complete(anyList(), actualTools.capture(), eq("required"), eq(false));
- assertThat(getToolName(actualTools.getValue().get(0)), CoreMatchers.is("database_gateway_search_metadata"));
- assertThat(getRequiredFields(actualTools.getValue().get(0)), CoreMatchers.is(List.of()));
- assertThat(getPropertyType(actualTools.getValue().get(0), "database"), CoreMatchers.is("string"));
- assertThat(getPropertyType(actualTools.getValue().get(0), "schema"), CoreMatchers.is("string"));
- assertThat(getPropertyType(actualTools.getValue().get(0), "query"), CoreMatchers.is("string"));
- assertThat(getPropertyType(actualTools.getValue().get(0), "object_types"), CoreMatchers.is("array"));
- assertThat(getNestedPropertyType(actualTools.getValue().get(0), "object_types", "items"), CoreMatchers.is("string"));
+ assertThat(getToolName(actualTools.getValue().get(0)), is("database_gateway_search_metadata"));
+ assertThat(getRequiredFields(actualTools.getValue().get(0)), is(List.of()));
+ assertThat(getPropertyType(actualTools.getValue().get(0), "database"), is("string"));
+ assertThat(getPropertyType(actualTools.getValue().get(0), "schema"), is("string"));
+ assertThat(getPropertyType(actualTools.getValue().get(0), "query"), is("string"));
+ assertThat(getPropertyType(actualTools.getValue().get(0), "object_types"), is("array"));
+ assertThat(getNestedPropertyType(actualTools.getValue().get(0), "object_types", "items"), is("string"));
}
@Test
void assertRunOffersOnlyNextMissingRequiredTool() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_search_metadata", MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(4);
- final String tableResourceUri = "shardingsphere://databases/logic_db/schemas/public/tables/orders";
- final Map<String, Object> searchArguments = Map.of("query", TABLE_NAME);
- final Map<String, Object> readResourceArguments = Map.of("uri", tableResourceUri);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ List<String> toolNames = List.of("database_gateway_search_metadata", MCPInteractionActionNames.READ_RESOURCE, "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(4);
+ String tableResourceUri = "shardingsphere://databases/logic_db/schemas/public/tables/orders";
+ Map<String, Object> searchArguments = Map.of("query", TABLE_NAME);
+ Map<String, Object> readResourceArguments = Map.of("uri", tableResourceUri);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
final ArgumentCaptor<List<Map<String, Object>>> actualTools = createToolDefinitionsCaptor();
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_search_metadata", searchArguments, "search-response"),
@@ -337,21 +339,21 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getMCPInteractionClient().call("database_gateway_execute_query", executeQueryArguments)).thenReturn(createResultSetPayload(2));
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
verify(getLLMChatClient(), times(3)).complete(anyList(), actualTools.capture(), eq("required"), eq(false));
- assertThat(getToolNames(actualTools.getAllValues().get(0)), CoreMatchers.is(List.of("database_gateway_search_metadata")));
- assertThat(getToolNames(actualTools.getAllValues().get(1)), CoreMatchers.is(List.of(MCPInteractionActionNames.READ_RESOURCE)));
- assertThat(getToolNames(actualTools.getAllValues().get(2)), CoreMatchers.is(List.of("database_gateway_execute_query")));
+ assertThat(getToolNames(actualTools.getAllValues().get(0)), is(List.of("database_gateway_search_metadata")));
+ assertThat(getToolNames(actualTools.getAllValues().get(1)), is(List.of(MCPInteractionActionNames.READ_RESOURCE)));
+ assertThat(getToolNames(actualTools.getAllValues().get(2)), is(List.of("database_gateway_execute_query")));
}
@Test
void assertRunAcceptsObjectInteractionSequence() throws IOException, InterruptedException {
final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
final LLMMCPConversationRunner actualRunner = createRunner(2);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
- final Map<String, Object> finalAnswerPayload = createFinalAnswerPayload(List.of(), 2);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ Map<String, Object> finalAnswerPayload = createFinalAnswerPayload(List.of(), 2);
finalAnswerPayload.put("interactionSequence", List.of(Map.of("tool", "database_gateway_execute_query", "arguments", executeQueryArguments)));
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"));
@@ -359,18 +361,18 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
new LLMChatCompletion(JsonUtils.toJsonString(finalAnswerPayload), List.of(), "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
}
@Test
void assertRunAcceptsCollapsedRepeatedInteractionSequence() throws IOException, InterruptedException {
- final List<String> toolNames = List.of("database_gateway_search_metadata", "database_gateway_execute_query");
- final LLME2EScenario actualScenario = createScenario(toolNames);
- final LLMMCPConversationRunner actualRunner = createRunner(2);
- final Map<String, Object> searchMetadataArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "query", TABLE_NAME);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ List<String> toolNames = List.of("database_gateway_search_metadata", "database_gateway_execute_query");
+ LLME2EScenario actualScenario = createScenario(toolNames);
+ LLMMCPConversationRunner actualRunner = createRunner(2);
+ Map<String, Object> searchMetadataArguments = Map.of("database", DATABASE_NAME, "schema", SCHEMA_NAME, "query", TABLE_NAME);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(new LLMChatCompletion("",
List.of(
new LLMToolCall("tool-1", "database_gateway_search_metadata", JsonUtils.toJsonString(searchMetadataArguments)),
@@ -382,7 +384,7 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
createFinalAnswerCompletion(toolNames, 2, "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
}
@@ -391,8 +393,8 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
void assertRunAcceptsSchemaQualifiedQuery() throws IOException, InterruptedException {
final LLME2EScenario actualScenario = createScenario(List.of("database_gateway_execute_query"));
final LLMMCPConversationRunner actualRunner = createRunner(2);
- final Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
- final Map<String, Object> finalAnswerPayload = createFinalAnswerPayload(List.of("database_gateway_execute_query"), 2);
+ Map<String, Object> executeQueryArguments = createExecuteQueryArguments(QUERY);
+ Map<String, Object> finalAnswerPayload = createFinalAnswerPayload(List.of("database_gateway_execute_query"), 2);
finalAnswerPayload.put("query", "SELECT COUNT(*) AS total_orders FROM public.orders");
when(getLLMChatClient().complete(anyList(), anyList(), eq("required"), eq(false))).thenReturn(
createToolCallCompletion("tool-1", "database_gateway_execute_query", executeQueryArguments, "tool-call-response"));
@@ -400,7 +402,7 @@ class LLMMCPConversationRunnerTest extends AbstractLLMMCPConversationRunnerTest
when(getLLMChatClient().complete(anyList(), eq(List.of()), eq("none"), eq(true))).thenReturn(
new LLMChatCompletion(JsonUtils.toJsonString(finalAnswerPayload), List.of(), "final-answer-response"));
- final LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
+ LLME2EArtifactBundle actual = actualRunner.run(actualScenario);
assertTrue(actual.getAssertionReport().isSuccess());
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatter.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatter.java
index 47f49ccc852..2f38a301c8e 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatter.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatter.java
@@ -31,7 +31,7 @@ import java.util.Objects;
final class LLMMCPModelFacingToolResponseFormatter {
static String format(final Map<String, Object> response) {
- final Map<String, Object> result = new LinkedHashMap<>(16, 1F);
+ Map<String, Object> result = new LinkedHashMap<>(16, 1F);
copyIfPresent(response, result, "response_mode");
copyIfPresent(response, result, "error_code");
copyIfPresent(response, result, "message");
@@ -71,13 +71,13 @@ final class LLMMCPModelFacingToolResponseFormatter {
}
private static void copyCompactItems(final Map<String, Object> source, final Map<String, Object> target) {
- final List<Map<String, Object>> items = LLMMCPJsonValues.castToList(source.get("items"));
+ List<Map<String, Object>> items = LLMMCPJsonValues.castToList(source.get("items"));
if (items.isEmpty()) {
return;
}
- final List<Map<String, Object>> compactItems = new LinkedList<>();
+ List<Map<String, Object>> compactItems = new LinkedList<>();
for (Map<String, Object> each : items.subList(0, Math.min(items.size(), 5))) {
- final Map<String, Object> compactItem = new LinkedHashMap<>(8, 1F);
+ Map<String, Object> compactItem = new LinkedHashMap<>(8, 1F);
copyIfPresent(each, compactItem, "database");
copyIfPresent(each, compactItem, "schema");
copyIfPresent(each, compactItem, "objectType");
@@ -92,13 +92,13 @@ final class LLMMCPModelFacingToolResponseFormatter {
}
private static void copyPromptList(final Map<String, Object> source, final Map<String, Object> target) {
- final List<Map<String, Object>> prompts = LLMMCPJsonValues.castToList(source.get("prompts"));
+ List<Map<String, Object>> prompts = LLMMCPJsonValues.castToList(source.get("prompts"));
if (prompts.isEmpty()) {
return;
}
- final List<String> names = new LinkedList<>();
+ List<String> names = new LinkedList<>();
for (Map<String, Object> each : prompts) {
- final String name = Objects.toString(each.get("name"), "").trim();
+ String name = Objects.toString(each.get("name"), "").trim();
if (!name.isEmpty()) {
names.add(name);
}
@@ -108,20 +108,20 @@ final class LLMMCPModelFacingToolResponseFormatter {
private static void copyPromptMessages(final Map<String, Object> source, final Map<String, Object> target) {
copyIfPresent(source, target, "description");
- final List<Object> messages = LLMMCPJsonValues.castToList(source.get("messages"));
+ List<Object> messages = LLMMCPJsonValues.castToList(source.get("messages"));
if (!messages.isEmpty()) {
target.put("message_count", messages.size());
}
}
private static void copyCompactArtifactList(final Map<String, Object> source, final Map<String, Object> target, final String fieldName) {
- final List<Map<String, Object>> artifacts = LLMMCPJsonValues.castToList(source.get(fieldName));
+ List<Map<String, Object>> artifacts = LLMMCPJsonValues.castToList(source.get(fieldName));
if (artifacts.isEmpty()) {
return;
}
- final List<Map<String, Object>> summaries = new LinkedList<>();
+ List<Map<String, Object>> summaries = new LinkedList<>();
for (Map<String, Object> each : artifacts.subList(0, Math.min(artifacts.size(), 3))) {
- final Map<String, Object> summary = new LinkedHashMap<>(4, 1F);
+ Map<String, Object> summary = new LinkedHashMap<>(4, 1F);
putArtifactCount(each, summary, "ddl_artifacts", "ddl_artifact_count");
putArtifactCount(each, summary, "index_plan", "index_plan_count");
putArtifactCount(each, summary, "distsql_artifacts", "distsql_artifact_count");
@@ -135,7 +135,7 @@ final class LLMMCPModelFacingToolResponseFormatter {
}
private static void putArtifactCount(final Map<String, Object> source, final Map<String, Object> target, final String sourceFieldName, final String targetFieldName) {
- final List<Object> artifacts = LLMMCPJsonValues.castToList(source.get(sourceFieldName));
+ List<Object> artifacts = LLMMCPJsonValues.castToList(source.get(sourceFieldName));
if (!artifacts.isEmpty()) {
target.put(targetFieldName, artifacts.size());
}
@@ -145,11 +145,11 @@ final class LLMMCPModelFacingToolResponseFormatter {
if (!source.containsKey("recovery")) {
return;
}
- final Map<String, Object> recovery = LLMMCPJsonValues.castToMap(source.get("recovery"));
+ Map<String, Object> recovery = LLMMCPJsonValues.castToMap(source.get("recovery"));
if (recovery.isEmpty()) {
return;
}
- final Map<String, Object> compactRecovery = new LinkedHashMap<>(8, 1F);
+ Map<String, Object> compactRecovery = new LinkedHashMap<>(8, 1F);
copyIfPresent(recovery, compactRecovery, "recovery_category");
copyIfPresent(recovery, compactRecovery, "category");
copyIfPresent(recovery, compactRecovery, "model_action");
@@ -162,11 +162,11 @@ final class LLMMCPModelFacingToolResponseFormatter {
}
private static void copyModelFacingNextActions(final Map<String, Object> source, final Map<String, Object> target) {
- final List<Map<String, Object>> nextActions = LLMMCPJsonValues.castToList(source.get("next_actions"));
+ List<Map<String, Object>> nextActions = LLMMCPJsonValues.castToList(source.get("next_actions"));
if (nextActions.isEmpty()) {
return;
}
- final List<Map<String, Object>> result = new LinkedList<>();
+ List<Map<String, Object>> result = new LinkedList<>();
for (Map<String, Object> each : nextActions) {
result.add(LLMMCPSideEffectNextAction.isExecutionAction(each) ? createSideEffectExecutionNextActionSummary(each) : each);
}
@@ -174,7 +174,7 @@ final class LLMMCPModelFacingToolResponseFormatter {
}
private static Map<String, Object> createSideEffectExecutionNextActionSummary(final Map<String, Object> action) {
- final Map<String, Object> result = new LinkedHashMap<>(4, 1F);
+ Map<String, Object> result = new LinkedHashMap<>(4, 1F);
copyIfPresent(action, result, "type");
copyIfPresent(action, result, "title");
copyIfPresent(action, result, "reason");
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPNextActions.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPNextActions.java
index 2f052f3ddf0..22016d78c4f 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPNextActions.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPNextActions.java
@@ -46,13 +46,13 @@ public final class LLMMCPNextActions {
return result;
}
- private static void appendActions(final List<Map<?, ?>> result, final Object value) {
+ private static void appendActions(final List<Map<?, ?>> actions, final Object value) {
if (!(value instanceof List)) {
return;
}
for (Object each : (List<?>) value) {
if (each instanceof Map) {
- result.add((Map<?, ?>) each);
+ actions.add((Map<?, ?>) each);
}
}
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPScenarioInference.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPScenarioInference.java
index 1c897e5467a..210f20ef0d6 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPScenarioInference.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPScenarioInference.java
@@ -37,11 +37,11 @@ final class LLMMCPScenarioInference {
private static final Pattern RESOURCE_URI_PATTERN = Pattern.compile("shardingsphere://[^`\\s,]+");
static String findExpectedResourceUri(final LLME2EScenario scenario) {
- final String expectedTableResourceUri = createExpectedTableResourceUri(scenario.getExpectedAnswer());
+ String expectedTableResourceUri = createExpectedTableResourceUri(scenario.getExpectedAnswer());
String promptResourceUri = "";
- final Matcher matcher = RESOURCE_URI_PATTERN.matcher(scenario.getUserPrompt());
+ Matcher matcher = RESOURCE_URI_PATTERN.matcher(scenario.getUserPrompt());
while (matcher.find()) {
- final String each = trimResourceUri(matcher.group());
+ String each = trimResourceUri(matcher.group());
if (each.equals(expectedTableResourceUri)) {
return each;
}
@@ -64,8 +64,8 @@ final class LLMMCPScenarioInference {
static String findLatestPlanId(final List<MCPInteractionTraceRecord> interactionTrace) {
for (int index = interactionTrace.size() - 1; index >= 0; index--) {
- final MCPInteractionTraceRecord each = interactionTrace.get(index);
- final String result = Objects.toString(each.getStructuredContent().get("plan_id"), "").trim();
+ MCPInteractionTraceRecord each = interactionTrace.get(index);
+ String result = Objects.toString(each.getStructuredContent().get("plan_id"), "").trim();
if (each.isValid() && !result.isEmpty() && !each.getStructuredContent().containsKey("error_code")) {
return result;
}
@@ -74,7 +74,7 @@ final class LLMMCPScenarioInference {
}
static String normalizeComparableQuery(final LLMStructuredAnswer expectedAnswer, final String query) {
- final String result = normalizeComparableQuery(query);
+ String result = normalizeComparableQuery(query);
if (expectedAnswer.getSchema().isEmpty() || expectedAnswer.getTable().isEmpty()) {
return result;
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPSideEffectNextAction.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPSideEffectNextAction.java
index 87abddd488f..3f7f6d0118c 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPSideEffectNextAction.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPSideEffectNextAction.java
@@ -30,9 +30,9 @@ final class LLMMCPSideEffectNextAction {
if (!"tool_call".equals(Objects.toString(action.get("type"), "").trim())) {
return false;
}
- final String toolName = Objects.toString(action.get("tool_name"), "").trim();
- final Map<String, Object> arguments = LLMMCPJsonValues.castToMap(action.get("arguments"));
- final String executionMode = Objects.toString(arguments.get("execution_mode"), "").trim();
+ String toolName = Objects.toString(action.get("tool_name"), "").trim();
+ Map<String, Object> arguments = LLMMCPJsonValues.castToMap(action.get("arguments"));
+ String executionMode = Objects.toString(arguments.get("execution_mode"), "").trim();
return "database_gateway_execute_update".equals(toolName) && "execute".equals(executionMode)
|| "database_gateway_apply_workflow".equals(toolName) && "review-then-execute".equals(executionMode);
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPToolCallNormalizer.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPToolCallNormalizer.java
index 43a666bf11b..b929c5932af 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPToolCallNormalizer.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPToolCallNormalizer.java
@@ -35,7 +35,7 @@ final class LLMMCPToolCallNormalizer {
static NormalizedToolCall normalize(final LLME2EScenario scenario, final String rawToolName, final Map<String, Object> rawArguments, final List<String> turnToolNames,
final List<MCPInteractionTraceRecord> interactionTrace) {
- final String toolName = normalizeToolName(rawToolName, rawArguments, turnToolNames);
+ String toolName = normalizeToolName(rawToolName, rawArguments, turnToolNames);
return new NormalizedToolCall(toolName, normalizeToolArguments(scenario, toolName, normalizeToolNameArguments(rawToolName, toolName, rawArguments), interactionTrace));
}
@@ -59,7 +59,7 @@ final class LLMMCPToolCallNormalizer {
if (rawToolName.equals(toolName) || !"database_gateway_execute_query".equals(toolName)) {
return rawArguments;
}
- final Map<String, Object> result = new LinkedHashMap<>(rawArguments);
+ Map<String, Object> result = new LinkedHashMap<>(rawArguments);
result.remove("execution_mode");
return result;
}
@@ -75,15 +75,15 @@ final class LLMMCPToolCallNormalizer {
}
private static Map<String, Object> normalizeResourceUriArgument(final LLME2EScenario scenario, final String toolName, final Map<String, Object> args) {
- final String resourceUriArgument = Objects.toString(args.get("uri"), "").trim();
+ String resourceUriArgument = Objects.toString(args.get("uri"), "").trim();
if (!MCPInteractionActionNames.READ_RESOURCE.equals(toolName) || resourceUriArgument.isEmpty() || resourceUriArgument.startsWith("shardingsphere://")) {
return args;
}
- final String resourceUri = LLMMCPScenarioInference.findExpectedResourceUri(scenario);
+ String resourceUri = LLMMCPScenarioInference.findExpectedResourceUri(scenario);
if (resourceUri.isEmpty()) {
return args;
}
- final Map<String, Object> result = new LinkedHashMap<>(args);
+ Map<String, Object> result = new LinkedHashMap<>(args);
result.put("uri", resourceUri);
return result;
}
@@ -92,14 +92,14 @@ final class LLMMCPToolCallNormalizer {
if (!"database_gateway_search_metadata".equals(toolName) || !hasExplicitExpectedScopeInstruction(scenario) || !shouldUseExpectedSearchScope(scenario.getExpectedAnswer(), args)) {
return args;
}
- final Map<String, Object> result = new LinkedHashMap<>(args);
+ Map<String, Object> result = new LinkedHashMap<>(args);
putIfBlank(result, "database", scenario.getExpectedAnswer().getDatabase());
putIfBlank(result, "schema", scenario.getExpectedAnswer().getSchema());
return result;
}
private static boolean hasExplicitExpectedScopeInstruction(final LLME2EScenario scenario) {
- final LLMStructuredAnswer expectedAnswer = scenario.getExpectedAnswer();
+ LLMStructuredAnswer expectedAnswer = scenario.getExpectedAnswer();
return scenario.getUserPrompt().contains(String.format(Locale.ENGLISH, "Use logical database `%s` and schema `%s`", expectedAnswer.getDatabase(), expectedAnswer.getSchema()));
}
@@ -110,7 +110,7 @@ final class LLMMCPToolCallNormalizer {
}
private static boolean shouldUseExpectedSearchScope(final LLMStructuredAnswer expectedAnswer, final Map<String, Object> args) {
- final String query = Objects.toString(args.get("query"), "").trim();
+ String query = Objects.toString(args.get("query"), "").trim();
if (expectedAnswer.getDatabase().isEmpty() || expectedAnswer.getSchema().isEmpty() || !expectedAnswer.getTable().equalsIgnoreCase(query)) {
return false;
}
@@ -121,14 +121,14 @@ final class LLMMCPToolCallNormalizer {
if (!"database_gateway_execute_query".equals(toolName) || !Objects.toString(args.get("schema"), "").trim().isEmpty()) {
return args;
}
- final LLMStructuredAnswer expectedAnswer = scenario.getExpectedAnswer();
+ LLMStructuredAnswer expectedAnswer = scenario.getExpectedAnswer();
if (expectedAnswer.getSchema().isEmpty()
|| !expectedAnswer.getDatabase().equals(Objects.toString(args.get("database"), "").trim())
|| !LLMMCPScenarioInference.normalizeComparableQuery(expectedAnswer, expectedAnswer.getQuery()).equals(
LLMMCPScenarioInference.normalizeComparableQuery(expectedAnswer, Objects.toString(args.get("sql"), "")))) {
return args;
}
- final Map<String, Object> result = new LinkedHashMap<>(args.size() + 1, 1F);
+ Map<String, Object> result = new LinkedHashMap<>(args.size() + 1, 1F);
result.putAll(args);
result.put("schema", expectedAnswer.getSchema());
return result;
@@ -138,7 +138,7 @@ final class LLMMCPToolCallNormalizer {
if (!toolName.startsWith(LLMMCPScenarioInference.PLANNING_TOOL_NAME_PREFIX) || !args.containsKey("plan_id") || !LLMMCPScenarioInference.findLatestPlanId(interactionTrace).isEmpty()) {
return args;
}
- final Map<String, Object> result = new LinkedHashMap<>(args);
+ Map<String, Object> result = new LinkedHashMap<>(args);
result.remove("plan_id");
return result;
}
@@ -147,17 +147,17 @@ final class LLMMCPToolCallNormalizer {
if (!("database_gateway_apply_workflow".equals(toolName) || "database_gateway_validate_workflow".equals(toolName)) || !isPlanIdPlaceholder(args.get("plan_id"))) {
return args;
}
- final String latestPlanId = LLMMCPScenarioInference.findLatestPlanId(interactionTrace);
+ String latestPlanId = LLMMCPScenarioInference.findLatestPlanId(interactionTrace);
if (latestPlanId.isEmpty()) {
return args;
}
- final Map<String, Object> result = new LinkedHashMap<>(args);
+ Map<String, Object> result = new LinkedHashMap<>(args);
result.put("plan_id", latestPlanId);
return result;
}
private static boolean isPlanIdPlaceholder(final Object value) {
- final String planId = Objects.toString(value, "").trim();
+ String planId = Objects.toString(value, "").trim();
return "plan_id".equals(planId) || "{plan_id}".equals(planId) || "<plan_id>".equals(planId) || planId.matches("\\d+");
}
@@ -165,11 +165,11 @@ final class LLMMCPToolCallNormalizer {
if (!MCPInteractionActionNames.COMPLETE.equals(toolName) || args.containsKey("reference")) {
return args;
}
- final Map<String, Object> latestReference = findLatestCompletionReference(interactionTrace);
+ Map<String, Object> latestReference = findLatestCompletionReference(interactionTrace);
if (latestReference.isEmpty()) {
return args;
}
- final Map<String, Object> result = new LinkedHashMap<>(args.size() + 1, 1F);
+ Map<String, Object> result = new LinkedHashMap<>(args.size() + 1, 1F);
result.put("reference", latestReference);
result.putAll(args);
return result;
@@ -177,11 +177,11 @@ final class LLMMCPToolCallNormalizer {
private static Map<String, Object> findLatestCompletionReference(final List<MCPInteractionTraceRecord> interactionTrace) {
for (int index = interactionTrace.size() - 1; index >= 0; index--) {
- final MCPInteractionTraceRecord each = interactionTrace.get(index);
+ MCPInteractionTraceRecord each = interactionTrace.get(index);
if (!each.isValid() || each.getStructuredContent().containsKey("error_code")) {
continue;
}
- final Map<String, Object> result = createCompletionReference(each);
+ Map<String, Object> result = createCompletionReference(each);
if (!result.isEmpty()) {
return result;
}
@@ -191,11 +191,11 @@ final class LLMMCPToolCallNormalizer {
private static Map<String, Object> createCompletionReference(final MCPInteractionTraceRecord traceRecord) {
if (MCPInteractionActionNames.GET_PROMPT.equals(traceRecord.getTargetName())) {
- final String promptName = Objects.toString(traceRecord.getArguments().get("name"), "").trim();
+ String promptName = Objects.toString(traceRecord.getArguments().get("name"), "").trim();
return promptName.isEmpty() ? Map.of() : Map.of("type", "ref/prompt", "name", promptName);
}
if (MCPInteractionActionNames.READ_RESOURCE.equals(traceRecord.getTargetName())) {
- final String resourceUri = Objects.toString(traceRecord.getArguments().get("uri"), "").trim();
+ String resourceUri = Objects.toString(traceRecord.getArguments().get("uri"), "").trim();
return resourceUri.isEmpty() ? Map.of() : Map.of("type", "ref/resource", "uri", resourceUri);
}
return Map.of();
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/scenario/LLMStructuredAnswer.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/scenario/LLMStructuredAnswer.java
index 078b8483209..e64a486cc09 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/scenario/LLMStructuredAnswer.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/scenario/LLMStructuredAnswer.java
@@ -53,7 +53,7 @@ public final class LLMStructuredAnswer {
* @return LLM structured answer
*/
public static LLMStructuredAnswer fromJson(final String json) {
- final Map<String, Object> payload = readPayload(json);
+ Map<String, Object> payload = readPayload(json);
List<String> interactionSequence = createInteractionSequence(payload);
return new LLMStructuredAnswer(
Objects.toString(payload.get("database"), "").trim(),
@@ -66,7 +66,7 @@ public final class LLMStructuredAnswer {
private static Map<String, Object> readPayload(final String json) {
try {
- final Map<String, Object> result = OBJECT_MAPPER.readValue(json, new TypeReference<>() {
+ Map<String, Object> result = OBJECT_MAPPER.readValue(json, new TypeReference<>() {
});
if (null == result) {
throw new IllegalArgumentException("Structured answer JSON must decode to one object.");
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/MCPBuilderEvaluationArtifactTest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/MCPBuilderEvaluationArtifactTest.java
index daf4458fa5b..1c77c02cf4a 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/MCPBuilderEvaluationArtifactTest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/MCPBuilderEvaluationArtifactTest.java
@@ -21,9 +21,12 @@ import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
@@ -288,18 +291,18 @@ final class MCPBuilderEvaluationArtifactTest {
return result;
}
- private Document loadDocument() throws Exception {
+ private Document loadDocument() throws ParserConfigurationException, SAXException, IOException {
try (InputStream inputStream = MCPBuilderEvaluationArtifactTest.class.getResourceAsStream("/llm/evaluation/mcp-builder-evaluation.xml")) {
assertNotNull(inputStream);
return parseDocument(inputStream);
}
}
- private Element loadQAPair(final String xml) throws Exception {
- return (Element) parseDocument(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))).getDocumentElement();
+ private Element loadQAPair(final String xml) throws ParserConfigurationException, SAXException, IOException {
+ return parseDocument(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))).getDocumentElement();
}
- private Document parseDocument(final InputStream inputStream) throws Exception {
+ private Document parseDocument(final InputStream inputStream) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
return factory.newDocumentBuilder().parse(inputStream);
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteRunner.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteRunner.java
index 6ad2442b322..914a1785048 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteRunner.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteRunner.java
@@ -85,7 +85,7 @@ final class LLMUsabilitySuiteRunner {
evaluatedScenarios.add(new EvaluatedScenario(each, metricCalculator.evaluateScenario(each, conversationResult.artifactBundle()),
conversationResult.artifactDirectory()));
}
- List<LLMUsabilityScenarioResult> scenarioResults = evaluatedScenarios.stream().map(EvaluatedScenario::result).toList();
+ List<LLMUsabilityScenarioResult> scenarioResults = evaluatedScenarios.stream().map(EvaluatedScenario::scenarioResult).toList();
LLMUsabilityScorecard scorecard = metricCalculator.createScorecard(suiteId, configuration.getRunId(), scenarioResults);
Path suiteDirectory = configuration.getArtifactRoot().resolve(configuration.getRunId()).resolve(suiteId);
reportWriter.writeScorecard(suiteDirectory, scorecard);
@@ -181,10 +181,10 @@ final class LLMUsabilitySuiteRunner {
}
private void assertScenarioInfrastructure(final EvaluatedScenario evaluatedScenario) {
- String failureType = evaluatedScenario.result().getFailureType();
+ String failureType = evaluatedScenario.scenarioResult().getFailureType();
assertFalse(INFRASTRUCTURE_FAILURE_TYPES.contains(failureType),
() -> evaluatedScenario.scenario().getScenarioId() + " failed deterministic infrastructure setup: " + failureType);
- assertFalse(evaluatedScenario.result().isApprovalViolation(), () -> evaluatedScenario.scenario().getScenarioId() + " attempted an unapproved side-effect path.");
+ assertFalse(evaluatedScenario.scenarioResult().isApprovalViolation(), () -> evaluatedScenario.scenario().getScenarioId() + " attempted an unapproved side-effect path.");
}
private void assertArtifactDirectory(final Path artifactDirectory) {
@@ -204,7 +204,7 @@ final class LLMUsabilitySuiteRunner {
}
private void assertTraceShape(final EvaluatedScenario evaluatedScenario) {
- for (MCPInteractionTraceRecord each : evaluatedScenario.result().getInteractionTrace()) {
+ for (MCPInteractionTraceRecord each : evaluatedScenario.scenarioResult().getInteractionTrace()) {
assertTrue(0 < each.getSequence(), () -> "Trace sequence must be positive in " + evaluatedScenario.scenario().getScenarioId());
assertFalse(each.getActionKind().isBlank(), () -> "Trace action kind is blank in " + evaluatedScenario.scenario().getScenarioId());
assertFalse(each.getActionOrigin().isBlank(), () -> "Trace action origin is blank in " + evaluatedScenario.scenario().getScenarioId());
@@ -269,6 +269,6 @@ final class LLMUsabilitySuiteRunner {
private record EvaluatedSuite(List<LLMUsabilityScenario> scenarios, List<EvaluatedScenario> evaluatedScenarios, LLMUsabilityScorecard scorecard) {
}
- private record EvaluatedScenario(LLMUsabilityScenario scenario, LLMUsabilityScenarioResult result, Path artifactDirectory) {
+ private record EvaluatedScenario(LLMUsabilityScenario scenario, LLMUsabilityScenarioResult scenarioResult, Path artifactDirectory) {
}
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculator.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculator.java
index 8d37f6b71ad..f012d9f56b6 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculator.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculator.java
@@ -87,7 +87,7 @@ public final class LLMUsabilityMetricCalculator {
message = "Scenario expected one recoverable MCP error with category `" + scenario.getExpectedRecoveryCategory() + "` before final success.";
}
boolean recoveredAfterError = success && (!scenario.isRecoveryExpected() || expectedRecoveryObserved);
- double queryAnswerFidelity = scenario.isQueryScenario() ? success ? 1.0D : 0.0D : 0.0D;
+ double queryAnswerFidelity = scenario.isQueryScenario() && success ? 1.0D : 0.0D;
boolean nextActionFollowed = isNextActionFollowed(interactionTrace);
boolean approvalViolation = hasApprovalViolation(interactionTrace);
boolean nativeToolCallCoverage = hasNativeRequiredToolCoverage(scenario.getLlmScenario().getRequiredToolNames(), interactionTrace);
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityReportWriter.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityReportWriter.java
index fdb2e354bf5..16720e51552 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityReportWriter.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityReportWriter.java
@@ -70,12 +70,12 @@ public final class LLMUsabilityReportWriter {
return result.toString();
}
- private void appendSummaryItem(final StringBuilder result, final String name, final Object value) {
- result.append("- ").append(name).append(": ").append(value).append(System.lineSeparator());
+ private void appendSummaryItem(final StringBuilder report, final String name, final Object value) {
+ report.append("- ").append(name).append(": ").append(value).append(System.lineSeparator());
}
- private void appendScenarioResult(final StringBuilder result, final LLMUsabilityScenarioResult scenarioResult) {
- result.append("- ").append(scenarioResult.getScenarioId())
+ private void appendScenarioResult(final StringBuilder report, final LLMUsabilityScenarioResult scenarioResult) {
+ report.append("- ").append(scenarioResult.getScenarioId())
.append(": ").append(scenarioResult.isSuccess() ? "PASS" : "FAIL")
.append(" failureType=").append(getTextOrDefault(scenarioResult.getFailureType()))
.append(", roundTrips=").append(scenarioResult.getRoundTripCount())
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionMySQLRuntimeE2ETest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionMySQLRuntimeE2ETest.java
index 02dfbc051ec..18269c66f46 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionMySQLRuntimeE2ETest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionMySQLRuntimeE2ETest.java
@@ -17,6 +17,11 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isA;
+
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
@@ -54,10 +59,6 @@ import java.util.Map;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.isA;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/PackagedDistributionE2ETest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/PackagedDistributionE2ETest.java
index 13c1eec3269..1464ccba44e 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/PackagedDistributionE2ETest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/PackagedDistributionE2ETest.java
@@ -17,6 +17,10 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+
import org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition;
import org.apache.shardingsphere.test.e2e.mcp.support.OfficialMCPToolNames;
import org.apache.shardingsphere.test.e2e.mcp.support.distribution.DockerImageHttpRuntime;
@@ -49,9 +53,6 @@ import java.util.Map;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLRuntimeE2ETest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLRuntimeE2ETest.java
index 77ba75fffbb..9b5d75911af 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLRuntimeE2ETest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLRuntimeE2ETest.java
@@ -17,6 +17,10 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+
import org.apache.shardingsphere.mcp.bootstrap.transport.MCPTransportConstants;
import org.apache.shardingsphere.mcp.support.markdown.MCPMarkdownResourceLoader;
import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
@@ -31,9 +35,6 @@ import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportContractE2ETest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportContractE2ETest.java
index e14349e0ea1..0f1e93de2c1 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportContractE2ETest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportContractE2ETest.java
@@ -17,6 +17,9 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.programmatic;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.is;
+
import org.apache.shardingsphere.mcp.support.descriptor.MCPShardingSphereMetadataKeys;
import org.apache.shardingsphere.mcp.support.workflow.descriptor.WorkflowToolDescriptors;
import org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition;
@@ -34,8 +37,6 @@ import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/distribution/PackagedDistributionTestSupportTest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/distribution/PackagedDistributionTestSupportTest.java
index 9980f148952..c08bdca004b 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/distribution/PackagedDistributionTestSupportTest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/distribution/PackagedDistributionTestSupportTest.java
@@ -17,6 +17,9 @@
package org.apache.shardingsphere.test.e2e.mcp.support.distribution;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.is;
+
import org.apache.shardingsphere.mcp.bootstrap.config.MCPLaunchConfiguration;
import org.apache.shardingsphere.mcp.bootstrap.config.MCPTransportType;
import org.apache.shardingsphere.mcp.bootstrap.config.loader.MCPConfigurationLoader;
@@ -35,8 +38,6 @@ import java.util.Optional;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -72,11 +73,11 @@ class PackagedDistributionTestSupportTest {
@Test
void assertFindDistributionHomeWithConfiguredHome() throws IOException {
- final Path configuredHome = Files.createDirectories(tempDir.resolve("configured-home"));
- final String actualOriginalHome = System.getProperty("mcp.distribution.home");
+ Path configuredHome = Files.createDirectories(tempDir.resolve("configured-home"));
+ String actualOriginalHome = System.getProperty("mcp.distribution.home");
System.setProperty("mcp.distribution.home", configuredHome.toString());
try {
- final Optional<Path> actual = PackagedDistributionTestSupport.findDistributionHome();
+ Optional<Path> actual = PackagedDistributionTestSupport.findDistributionHome();
assertThat(actual.orElseThrow(), is(configuredHome.toAbsolutePath().normalize()));
} finally {
restoreDistributionHome(actualOriginalHome);
@@ -85,13 +86,13 @@ class PackagedDistributionTestSupportTest {
@Test
void assertFindDistributionHomeWithRelativeConfiguredHome() throws IOException {
- final Path configuredHome = Files.createDirectories(tempDir.resolve("distribution-home"));
- final String actualOriginalHome = System.getProperty("mcp.distribution.home");
- final String actualOriginalProjectDirectory = System.getProperty("maven.multiModuleProjectDirectory");
+ Path configuredHome = Files.createDirectories(tempDir.resolve("distribution-home"));
+ String actualOriginalHome = System.getProperty("mcp.distribution.home");
+ String actualOriginalProjectDirectory = System.getProperty("maven.multiModuleProjectDirectory");
System.setProperty("mcp.distribution.home", "distribution-home");
System.setProperty("maven.multiModuleProjectDirectory", tempDir.toString());
try {
- final Optional<Path> actual = PackagedDistributionTestSupport.findDistributionHome();
+ Optional<Path> actual = PackagedDistributionTestSupport.findDistributionHome();
assertThat(actual.orElseThrow(), is(configuredHome.toAbsolutePath().normalize()));
} finally {
restoreDistributionHome(actualOriginalHome);
@@ -101,8 +102,8 @@ class PackagedDistributionTestSupportTest {
@Test
void assertFindDistributionHomeWithMissingConfiguredHome() {
- final Path configuredHome = tempDir.resolve("missing-home");
- final String actualOriginalHome = System.getProperty("mcp.distribution.home");
+ Path configuredHome = tempDir.resolve("missing-home");
+ String actualOriginalHome = System.getProperty("mcp.distribution.home");
System.setProperty("mcp.distribution.home", configuredHome.toString());
try {
assertThrows(IllegalStateException.class, PackagedDistributionTestSupport::findDistributionHome);
@@ -113,14 +114,14 @@ class PackagedDistributionTestSupportTest {
@Test
void assertPrepareFailsWithMissingDistributionHome() throws IOException {
- final Path repositoryRoot = tempDir.resolve("repo");
+ Path repositoryRoot = tempDir.resolve("repo");
Files.createDirectories(repositoryRoot.resolve("distribution/mcp"));
- final String actualOriginalHome = System.getProperty("mcp.distribution.home");
- final String actualOriginalProjectDirectory = System.getProperty("maven.multiModuleProjectDirectory");
+ String actualOriginalHome = System.getProperty("mcp.distribution.home");
+ String actualOriginalProjectDirectory = System.getProperty("maven.multiModuleProjectDirectory");
System.clearProperty("mcp.distribution.home");
System.setProperty("maven.multiModuleProjectDirectory", repositoryRoot.toString());
try {
- final IllegalStateException actual = assertThrows(IllegalStateException.class,
+ IllegalStateException actual = assertThrows(IllegalStateException.class,
() -> PackagedDistributionTestSupport.prepare(tempDir.resolve("missing-distribution"), RuntimeTransport.HTTP));
assertThat(actual.getMessage(), is("Packaged MCP distribution was not found. Run `./mvnw -pl distribution/mcp -am -DskipTests package` first"
+ " or set `-Dmcp.distribution.home=/path/to/apache-shardingsphere-mcp-*`. Checked `"
@@ -134,11 +135,11 @@ class PackagedDistributionTestSupportTest {
@ParameterizedTest(name = "{0}")
@MethodSource("prepareCases")
void assertPrepareWithTransport(final String caseName, final RuntimeTransport transport, final MCPTransportType expectedTransportType) throws IOException {
- final Path distributionHome = createDistributionHome(tempDir.resolve(caseName));
- final String actualOriginalHome = System.getProperty("mcp.distribution.home");
+ Path distributionHome = createDistributionHome(tempDir.resolve(caseName));
+ String actualOriginalHome = System.getProperty("mcp.distribution.home");
System.setProperty("mcp.distribution.home", distributionHome.toString());
try {
- final PreparedPackagedDistribution actual = PackagedDistributionTestSupport.prepare(tempDir.resolve(caseName + "-working"), transport);
+ PreparedPackagedDistribution actual = PackagedDistributionTestSupport.prepare(tempDir.resolve(caseName + "-working"), transport);
final MCPLaunchConfiguration actualConfig = MCPConfigurationLoader.load(actual.configFile().toString());
assertThat(actual.transport(), is(transport));
assertFalse(Files.exists(actual.home().resolve("data")));
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/runtime/MySQLRuntimeTestSupport.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/runtime/MySQLRuntimeTestSupport.java
index c116155af7f..42e7ff6235b 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/runtime/MySQLRuntimeTestSupport.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/runtime/MySQLRuntimeTestSupport.java
@@ -116,11 +116,11 @@ public final class MySQLRuntimeTestSupport {
* @return Docker-required message
*/
public static String createDockerRequiredMessage(final String scenarioMessage) {
- return createDockerRequiredMessage(scenarioMessage, getDockerUnavailableReason());
+ return createDockerRequiredMessage(scenarioMessage, getDockerUnavailableReason().orElse(""));
}
- static String createDockerRequiredMessage(final String scenarioMessage, final Optional<String> unavailableReason) {
- return unavailableReason.map(each -> scenarioMessage + " Docker readiness diagnostic: " + each).orElse(scenarioMessage);
+ static String createDockerRequiredMessage(final String scenarioMessage, final String unavailableReason) {
+ return unavailableReason.isEmpty() ? scenarioMessage : scenarioMessage + " Docker readiness diagnostic: " + unavailableReason;
}
private static String createDockerUnavailableReason(final IllegalStateException ex) {
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/runtime/MySQLRuntimeTestSupportTest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/runtime/MySQLRuntimeTestSupportTest.java
index 4847ae87391..eb7ea9c1354 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/runtime/MySQLRuntimeTestSupportTest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/runtime/MySQLRuntimeTestSupportTest.java
@@ -19,8 +19,6 @@ package org.apache.shardingsphere.test.e2e.mcp.support.runtime;
import org.junit.jupiter.api.Test;
-import java.util.Optional;
-
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@@ -28,12 +26,12 @@ class MySQLRuntimeTestSupportTest {
@Test
void assertCreateDockerRequiredMessageWithReadinessDiagnostic() {
- assertThat(MySQLRuntimeTestSupport.createDockerRequiredMessage("Docker is required.", Optional.of("daemon unavailable")),
+ assertThat(MySQLRuntimeTestSupport.createDockerRequiredMessage("Docker is required.", "daemon unavailable"),
is("Docker is required. Docker readiness diagnostic: daemon unavailable"));
}
@Test
void assertCreateDockerRequiredMessageWithoutReadinessDiagnostic() {
- assertThat(MySQLRuntimeTestSupport.createDockerRequiredMessage("Docker is required.", Optional.empty()), is("Docker is required."));
+ assertThat(MySQLRuntimeTestSupport.createDockerRequiredMessage("Docker is required.", ""), is("Docker is required."));
}
}