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/08/01 14:15:48 UTC
(shardingsphere) branch master updated: Refactor MCP module to remove unnecessary abstractions (#39308)
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 ec6ad179135 Refactor MCP module to remove unnecessary abstractions (#39308)
ec6ad179135 is described below
commit ec6ad179135ae69bab713edea5886f816e3063ff
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Sat Aug 1 22:15:40 2026 +0800
Refactor MCP module to remove unnecessary abstractions (#39308)
- remove fixed LLM token limits and redundant response adapters
- simplify workflow contracts and inline single-owner helpers
- consolidate test fixtures and remove shape-only tests
---
.../resource/MCPResourceDescriptorTest.java | 7 -
.../capability/tool/MCPCallToolResultFactory.java | 28 ++-
.../capability/tool/MCPResourceLinkContract.java | 59 -----
.../server/http/SessionAttributionResolver.java | 10 +-
.../server/http/StreamableHttpMCPServlet.java | 9 +-
.../ServerTransportSecurityValidatorFactory.java | 47 ----
.../transport/server/MCPSyncServerFactoryTest.java | 15 +-
...erverTransportSecurityValidatorFactoryTest.java | 110 ----------
.../mcp/core/handler/core/CoreHandlerProvider.java | 18 +-
.../mcp/core/handler/core/CoreToolHandlers.java | 52 -----
.../resource/handler/MCPResourceDefinition.java | 38 ----
.../handler/ResourceDefinitionRegistry.java | 30 ++-
.../workflow/WorkflowExecutionToolHandler.java | 2 +-
.../workflow/WorkflowValidationToolHandler.java | 2 +-
.../mcp/core/workflow/WorkflowApplyOutcome.java | 2 +-
.../core/workflow/WorkflowExecutionService.java | 33 ++-
.../WorkflowRuntimeDefinitionRegistry.java | 6 +-
.../handler/MetadataCompletionHandlerTest.java | 5 -
.../WorkflowPlanIdCompletionHandlerTest.java | 5 -
.../capability/ServerCapabilitiesHandlerTest.java | 6 +-
.../metadata/MetadataResourceHandlerTest.java | 10 -
.../mcp/core/resource/uri/MCPUriPatternTest.java | 6 -
.../workflow/WorkflowExecutionToolHandlerTest.java | 9 +-
.../workflow/WorkflowHandlerTestFixture.java | 17 +-
.../WorkflowValidationToolHandlerTest.java | 12 +-
.../workflow/WorkflowExecutionServiceTest.java | 65 +++---
.../WorkflowRuntimeDefinitionRegistryTest.java | 27 +--
.../broadcast/BroadcastFeatureDefinitionTest.java | 10 -
.../broadcast/BroadcastMCPHandlerProviderTest.java | 3 +-
.../feature/encrypt/EncryptMCPHandlerProvider.java | 2 +-
.../EncryptWorkflowToolResponseBuilder.java | 69 ------
.../tool/handler/PlanEncryptRuleToolHandler.java | 37 +++-
.../service/EncryptWorkflowPlanningService.java | 2 +-
.../service/EncryptWorkflowValidationService.java | 2 +-
.../encrypt/EncryptFeatureDefinitionTest.java | 36 ---
.../encrypt/EncryptMCPHandlerProviderTest.java | 3 +-
.../EncryptAlgorithmCompletionHandlerTest.java | 5 -
.../handler/MaskWorkflowToolResponseBuilder.java | 53 -----
.../mask/tool/handler/PlanMaskRuleToolHandler.java | 19 +-
.../service/MaskWorkflowValidationService.java | 2 +-
.../feature/mask/MaskFeatureDefinitionTest.java | 10 -
.../feature/mask/MaskMCPHandlerProviderTest.java | 3 +-
.../MaskAlgorithmCompletionHandlerTest.java | 5 -
.../ReadwriteSplittingFeatureDefinitionTest.java | 6 -
.../ReadwriteSplittingMCPHandlerProviderTest.java | 4 +-
...gLoadBalanceAlgorithmCompletionHandlerTest.java | 5 -
.../shadow/ShadowFeatureDefinitionTest.java | 33 ---
.../ShadowAlgorithmCompletionHandlerTest.java | 5 -
.../sharding/ShardingMCPHandlerProvider.java | 12 +-
.../handler/ShardingPlanningRequestBinder.java | 18 +-
.../service/ShardingWorkflowLifecycleSpec.java | 49 -----
.../service/ShardingWorkflowPlanningService.java | 22 +-
.../sharding/ShardingFeatureDefinitionTest.java | 45 ----
.../sharding/ShardingMCPHandlerProviderTest.java | 3 +-
.../ShardingAlgorithmCompletionHandlerTest.java | 5 -
.../descriptor/MCPGuidancePayloadBuilder.java | 17 +-
.../support/security/MCPClientSafetyPolicy.java | 49 -----
.../support/workflow/WorkflowPropertySource.java | 55 -----
.../workflow/model/RuleWorkflowFeatureData.java | 35 +--
.../workflow/model/WorkflowContextSnapshot.java | 4 +-
.../workflow/model/WorkflowFeatureData.java | 31 ---
.../support/workflow/model/WorkflowRequest.java | 24 +-
.../workflow/service/WorkflowArtifactBundle.java | 16 +-
.../service/WorkflowArtifactMaskUtils.java | 54 ++---
.../service/WorkflowArtifactPayloadUtils.java | 8 +-
.../service/WorkflowPlanPayloadBuilder.java | 7 +-
.../workflow/service/WorkflowPlanningSupport.java | 5 +-
.../service/WorkflowSecretReferenceUtils.java | 38 ++--
.../MCPWorkflowApplySynchronizationHandler.java | 42 ----
.../workflow/spi/MCPWorkflowRuntimeHandler.java | 36 ++-
.../workflow/spi/MCPWorkflowValidationHandler.java | 47 ----
.../workflow/spi/WorkflowRuntimeDefinition.java | 22 +-
.../jdbc/RuntimeDatabaseConfigurationTest.java | 9 -
.../metadata/query/DatabaseTestDataFactory.java | 241 ---------------------
.../metadata/query/MetadataQueryServiceTest.java | 183 +++++++++++++++-
.../diagnostic/MCPDiagnosticCategoryTest.java | 64 ------
.../protocol/payload/MCPMapPayloadTest.java | 35 ---
.../security/MCPClientSafetyPolicyTest.java | 43 ----
.../model/WorkflowContextSnapshotTest.java | 11 +-
.../service/WorkflowArtifactBundleTest.java | 33 ++-
.../service/WorkflowArtifactMaskUtilsTest.java | 54 ++---
.../service/WorkflowSecretReferenceUtilsTest.java | 44 +---
.../AbstractFunctionalityE2ETest.java | 35 ---
.../functionality/AbstractMySQLRuntimeE2ETest.java | 21 +-
.../AbstractTransportParameterizedE2ETest.java | 18 +-
.../functionality/FunctionalityTransportCases.java | 66 ------
.../llm/conversation/LLMConversationRunner.java | 26 ++-
.../mcp/llm/conversation/LLMMCPActionExecutor.java | 49 -----
.../e2e/mcp/llm/conversation/LLMMCPJsonValues.java | 25 +--
.../LLMMCPModelFacingToolResponseFormatter.java | 226 -------------------
.../conversation/client/LLMChatModelClient.java | 24 +-
.../e2e/mcp/llm/fixture/LLMRuntimeSupport.java | 2 +-
92 files changed, 701 insertions(+), 2096 deletions(-)
diff --git a/mcp/api/src/test/java/org/apache/shardingsphere/mcp/api/capability/resource/MCPResourceDescriptorTest.java b/mcp/api/src/test/java/org/apache/shardingsphere/mcp/api/capability/resource/MCPResourceDescriptorTest.java
index f3bda2068fc..56cb0801f5c 100644
--- a/mcp/api/src/test/java/org/apache/shardingsphere/mcp/api/capability/resource/MCPResourceDescriptorTest.java
+++ b/mcp/api/src/test/java/org/apache/shardingsphere/mcp/api/capability/resource/MCPResourceDescriptorTest.java
@@ -20,7 +20,6 @@ package org.apache.shardingsphere.mcp.api.capability.resource;
import org.junit.jupiter.api.Test;
import java.util.Collections;
-import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@@ -45,10 +44,4 @@ class MCPResourceDescriptorTest {
assertTrue(actual.isTemplated());
}
- @Test
- void assertMetaIsKeptAsMetadataOnly() {
- MCPResourceDescriptor actual = new MCPResourceDescriptor("shardingsphere://features/encrypt/algorithms", "encrypt-algorithms", "Encrypt Algorithms",
- "List encrypt algorithms.", "application/json", MCPResourceAnnotations.EMPTY, Map.of("org.apache.shardingsphere/runtime-visibility", "ShardingSphere-Proxy"));
- assertThat(actual.getMeta(), is(Map.of("org.apache.shardingsphere/runtime-visibility", "ShardingSphere-Proxy")));
- }
}
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 4fca8506ae7..96ea0aa5310 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
@@ -21,12 +21,14 @@ import io.modelcontextprotocol.json.schema.JsonSchemaValidator;
import io.modelcontextprotocol.json.schema.JsonSchemaValidator.ValidationResponse;
import io.modelcontextprotocol.json.schema.jackson2.DefaultJsonSchemaValidator;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
+import io.modelcontextprotocol.spec.McpSchema;
import org.apache.shardingsphere.infra.util.json.JsonUtils;
import org.apache.shardingsphere.mcp.api.payload.MCPSuccessPayload;
import org.apache.shardingsphere.mcp.api.capability.tool.MCPToolDescriptor;
import org.apache.shardingsphere.mcp.core.protocol.error.MCPErrorPayload;
import org.apache.shardingsphere.mcp.support.descriptor.MCPShardingSphereMetadataKeys;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
@@ -65,7 +67,7 @@ final class MCPCallToolResultFactory {
MCPResourceLinkCandidateCollector.ResourceLinkCandidates candidates = new MCPResourceLinkCandidateCollector(RESOURCE_LINK_LIMIT).collect(payload);
int emittedCount = 0;
for (MCPResourceLinkCandidateCollector.ResourceLinkCandidate each : candidates.candidates()) {
- builder.addContent(MCPResourceLinkContract.createResourceLink(each));
+ builder.addContent(createResourceLink(each));
emittedCount++;
}
if (0 < candidates.totalCount()) {
@@ -79,4 +81,28 @@ final class MCPCallToolResultFactory {
MCPShardingSphereMetadataKeys.RESOURCE_LINKS_OMITTED, Math.max(0, totalCount - emittedCount),
MCPShardingSphereMetadataKeys.RESOURCE_LINK_LIMIT, RESOURCE_LINK_LIMIT);
}
+
+ private McpSchema.ResourceLink createResourceLink(final MCPResourceLinkCandidateCollector.ResourceLinkCandidate candidate) {
+ return McpSchema.ResourceLink.builder()
+ .name(resolveResourceLinkName(candidate.uri()))
+ .title(candidate.title())
+ .uri(candidate.uri())
+ .description(candidate.description())
+ .mimeType("application/json")
+ .meta(createResourceLinkMeta(candidate))
+ .build();
+ }
+
+ private String resolveResourceLinkName(final String uri) {
+ int separatorIndex = uri.lastIndexOf('/');
+ return separatorIndex < 0 || separatorIndex == uri.length() - 1 ? uri : uri.substring(separatorIndex + 1);
+ }
+
+ private Map<String, Object> createResourceLinkMeta(final MCPResourceLinkCandidateCollector.ResourceLinkCandidate candidate) {
+ Map<String, Object> result = new LinkedHashMap<>(3, 1F);
+ result.put(MCPShardingSphereMetadataKeys.RESOURCE_KIND, candidate.resourceKind());
+ result.put(MCPShardingSphereMetadataKeys.PURPOSE, candidate.purpose());
+ result.put(MCPShardingSphereMetadataKeys.SOURCE_FIELD, candidate.sourceField());
+ return result;
+ }
}
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkContract.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkContract.java
deleted file mode 100644
index dbd9ce9eace..00000000000
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkContract.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.bootstrap.transport.capability.tool;
-
-import io.modelcontextprotocol.spec.McpSchema;
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.mcp.support.descriptor.MCPShardingSphereMetadataKeys;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-final class MCPResourceLinkContract {
-
- private static final String JSON_CONTENT_TYPE = "application/json";
-
- static McpSchema.ResourceLink createResourceLink(final MCPResourceLinkCandidateCollector.ResourceLinkCandidate candidate) {
- return McpSchema.ResourceLink.builder()
- .name(resolveResourceLinkName(candidate.uri()))
- .title(candidate.title())
- .uri(candidate.uri())
- .description(candidate.description())
- .mimeType(JSON_CONTENT_TYPE)
- .meta(createResourceLinkMeta(candidate))
- .build();
- }
-
- private static String resolveResourceLinkName(final String uri) {
- int separatorIndex = uri.lastIndexOf('/');
- if (separatorIndex < 0 || separatorIndex == uri.length() - 1) {
- return uri;
- }
- return uri.substring(separatorIndex + 1);
- }
-
- private static Map<String, Object> createResourceLinkMeta(final MCPResourceLinkCandidateCollector.ResourceLinkCandidate candidate) {
- Map<String, Object> result = new LinkedHashMap<>(3, 1F);
- result.put(MCPShardingSphereMetadataKeys.RESOURCE_KIND, candidate.resourceKind());
- result.put(MCPShardingSphereMetadataKeys.PURPOSE, candidate.purpose());
- result.put(MCPShardingSphereMetadataKeys.SOURCE_FIELD, candidate.sourceField());
- return result;
- }
-}
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/SessionAttributionResolver.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/SessionAttributionResolver.java
index 48f921b546b..0e1570464c3 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/SessionAttributionResolver.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/SessionAttributionResolver.java
@@ -31,6 +31,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.function.Function;
/**
* Session attribution resolver.
@@ -98,7 +99,7 @@ public final class SessionAttributionResolver {
return !isEnabled() ? "disabled" : String.format("trusted-header:%s", config.getSubjectHeader());
}
- private Map<String, String> resolveAttributes(final Iterable<String> headerNames, final HeaderValueReader headerValueReader) {
+ private Map<String, String> resolveAttributes(final Iterable<String> headerNames, final Function<String, String> headerValueReader) {
Map<String, String> result = new LinkedHashMap<>();
String attributeHeaderPrefix = config.getAttributeHeaderPrefix();
if (attributeHeaderPrefix.isEmpty()) {
@@ -108,7 +109,7 @@ public final class SessionAttributionResolver {
for (String each : headerNames) {
String actualHeaderName = Objects.toString(each, "").trim();
if (actualHeaderName.toLowerCase(Locale.ENGLISH).startsWith(normalizedPrefix)) {
- result.put(actualHeaderName.substring(attributeHeaderPrefix.length()).toLowerCase(Locale.ENGLISH), headerValueReader.read(actualHeaderName));
+ result.put(actualHeaderName.substring(attributeHeaderPrefix.length()).toLowerCase(Locale.ENGLISH), headerValueReader.apply(actualHeaderName));
}
}
return result;
@@ -127,9 +128,4 @@ public final class SessionAttributionResolver {
return "";
}
- @FunctionalInterface
- private interface HeaderValueReader {
-
- String read(String headerName);
- }
}
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
index 6a432bdd9f8..d457f47af23 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServlet.java
@@ -34,10 +34,13 @@ import jakarta.servlet.http.HttpServletResponseWrapper;
import org.apache.shardingsphere.mcp.api.session.MCPSessionIdentity;
import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
import org.apache.shardingsphere.mcp.bootstrap.config.HttpTransportConfiguration;
+import org.apache.shardingsphere.mcp.bootstrap.transport.HttpTransportHostUtils;
import org.apache.shardingsphere.mcp.bootstrap.transport.MCPTransportConstants;
import org.apache.shardingsphere.mcp.bootstrap.transport.MCPTransportErrorFactory;
import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.MCPTransportSecurityException;
-import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.ServerTransportSecurityValidatorFactory;
+import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.ShardingSphereServerTransportSecurityValidator;
+import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.constraint.OriginHeaderConstraint;
+import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.constraint.ProtocolVersionHeaderConstraint;
import org.apache.shardingsphere.mcp.core.session.MCPSessionExecutionCoordinator;
import org.apache.shardingsphere.mcp.core.session.MCPSessionManager;
import reactor.core.publisher.Mono;
@@ -83,7 +86,9 @@ final class StreamableHttpMCPServlet extends HttpServlet implements McpStreamabl
StreamableHttpMCPServlet(final MCPSessionManager sessionManager, final McpJsonMapper jsonMapper, final HttpTransportConfiguration config) {
sessionAttributionResolver = new SessionAttributionResolver(config.getSessionAttributionSource());
- securityValidator = ServerTransportSecurityValidatorFactory.create(sessionManager, config.getBindHost(), sessionAttributionResolver);
+ securityValidator = new ShardingSphereServerTransportSecurityValidator(sessionManager,
+ new OriginHeaderConstraint(HttpTransportHostUtils.isLoopbackHost(config.getBindHost())),
+ new ProtocolVersionHeaderConstraint(), sessionAttributionResolver);
delegate = createDelegate(jsonMapper, config.getEndpointPath());
this.jsonMapper = jsonMapper;
this.sessionManager = sessionManager;
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/ServerTransportSecurityValidatorFactory.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/ServerTransportSecurityValidatorFactory.java
deleted file mode 100644
index b667e723196..00000000000
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/ServerTransportSecurityValidatorFactory.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator;
-
-import io.modelcontextprotocol.server.transport.ServerTransportSecurityValidator;
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.mcp.bootstrap.transport.HttpTransportHostUtils;
-import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.SessionAttributionResolver;
-import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.constraint.OriginHeaderConstraint;
-import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator.constraint.ProtocolVersionHeaderConstraint;
-import org.apache.shardingsphere.mcp.core.session.MCPSessionManager;
-
-/**
- * Server transport security validator factory.
- */
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public final class ServerTransportSecurityValidatorFactory {
-
- /**
- * Create the MCP-facing transport security validator.
- *
- * @param sessionManager session manager
- * @param bindHost bind host
- * @param sessionAttributionResolver session attribution resolver
- * @return transport security validator
- */
- public static ServerTransportSecurityValidator create(final MCPSessionManager sessionManager, final String bindHost, final SessionAttributionResolver sessionAttributionResolver) {
- return new ShardingSphereServerTransportSecurityValidator(sessionManager, new OriginHeaderConstraint(HttpTransportHostUtils.isLoopbackHost(bindHost)),
- new ProtocolVersionHeaderConstraint(), sessionAttributionResolver);
- }
-}
diff --git a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/MCPSyncServerFactoryTest.java b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/MCPSyncServerFactoryTest.java
index fd652ed2778..a9bc4fd8af5 100644
--- a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/MCPSyncServerFactoryTest.java
+++ b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/MCPSyncServerFactoryTest.java
@@ -24,9 +24,7 @@ import io.modelcontextprotocol.server.McpServerFeatures.SyncResourceTemplateSpec
import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.spec.McpSchema;
-import io.modelcontextprotocol.spec.DefaultMcpStreamableServerSessionFactory;
import io.modelcontextprotocol.spec.McpServerSession;
-import io.modelcontextprotocol.spec.McpServerTransport;
import io.modelcontextprotocol.spec.McpServerTransportProvider;
import io.modelcontextprotocol.spec.McpStreamableServerSession;
import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider;
@@ -42,10 +40,8 @@ import org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalogInde
import org.apache.shardingsphere.mcp.support.markdown.MCPMarkdownResourceLoader;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
-import org.mockito.internal.configuration.plugins.Plugins;
import reactor.core.publisher.Mono;
-import java.time.Duration;
import java.util.List;
import java.util.Map;
@@ -62,7 +58,7 @@ import static org.mockito.Mockito.when;
class MCPSyncServerFactoryTest {
@Test
- void assertCreateWithTransportProvider() throws NoSuchFieldException, IllegalAccessException {
+ void assertCreateWithTransportProvider() {
TestServerTransportProvider transportProvider = new TestServerTransportProvider();
McpSyncServer actual = createFactory().create(transportProvider);
assertNotNull(transportProvider.sessionFactory);
@@ -72,20 +68,17 @@ class MCPSyncServerFactoryTest {
assertThat(actual.listResources().size(), is(1));
assertThat(actual.listResourceTemplates().size(), is(1));
assertThat(actual.listPrompts().size(), is(1));
- McpServerSession session = transportProvider.sessionFactory.create(mock(McpServerTransport.class));
- assertThat(getRequestTimeout(McpServerSession.class, session), is(Duration.ofMinutes(10L)));
actual.closeGracefully();
}
@Test
- void assertCreateWithStreamableTransportProvider() throws NoSuchFieldException, IllegalAccessException {
+ void assertCreateWithStreamableTransportProvider() {
TestStreamableTransportProvider transportProvider = new TestStreamableTransportProvider();
McpSyncServer actual = createFactory().create(transportProvider);
assertNotNull(transportProvider.sessionFactory);
assertThat(actual.listTools().size(), is(1));
assertThat(actual.listResources().size(), is(1));
assertThat(actual.listResourceTemplates().size(), is(1));
- assertThat(getRequestTimeout(DefaultMcpStreamableServerSessionFactory.class, transportProvider.sessionFactory), is(Duration.ofMinutes(10L)));
actual.closeGracefully();
}
@@ -137,10 +130,6 @@ class MCPSyncServerFactoryTest {
}
}
- private Duration getRequestTimeout(final Class<?> targetClass, final Object target) throws NoSuchFieldException, IllegalAccessException {
- return (Duration) Plugins.getMemberAccessor().get(targetClass.getDeclaredField("requestTimeout"), target);
- }
-
private McpSchema.CallToolResult createFixtureToolResult() {
return McpSchema.CallToolResult.builder().addTextContent("ok").build();
}
diff --git a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/ServerTransportSecurityValidatorFactoryTest.java b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/ServerTransportSecurityValidatorFactoryTest.java
deleted file mode 100644
index 98171ede482..00000000000
--- a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/validator/ServerTransportSecurityValidatorFactoryTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.bootstrap.transport.server.http.validator;
-
-import io.modelcontextprotocol.server.transport.ServerTransportSecurityException;
-import io.modelcontextprotocol.server.transport.ServerTransportSecurityValidator;
-import org.apache.shardingsphere.mcp.bootstrap.transport.server.http.SessionAttributionResolver;
-import org.apache.shardingsphere.mcp.core.session.MCPSessionManager;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-import java.util.Map;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-
-class ServerTransportSecurityValidatorFactoryTest {
-
- private static final SessionAttributionResolver DISABLED_SESSION_ATTRIBUTION_RESOLVER = new SessionAttributionResolver(null);
-
- @Test
- void assertCreateWithoutOptionalRules() {
- MCPSessionManager sessionManager = mock(MCPSessionManager.class);
- ServerTransportSecurityValidator actual = ServerTransportSecurityValidatorFactory.create(sessionManager, "127.0.0.1", DISABLED_SESSION_ATTRIBUTION_RESOLVER);
- assertDoesNotThrow(() -> actual.validateHeaders(Map.of()));
- verifyNoInteractions(sessionManager);
- }
-
- @Test
- void assertCreateWithLoopbackOrigin() {
- MCPSessionManager sessionManager = mock(MCPSessionManager.class);
- ServerTransportSecurityValidator actual = ServerTransportSecurityValidatorFactory.create(sessionManager, "127.0.0.1", DISABLED_SESSION_ATTRIBUTION_RESOLVER);
- assertDoesNotThrow(() -> actual.validateHeaders(Map.of("Origin", List.of("http://127.0.0.1:8080"))));
- verifyNoInteractions(sessionManager);
- }
-
- @Test
- void assertCreateWithLoopbackOriginRejectsRemoteOrigin() {
- MCPSessionManager sessionManager = mock(MCPSessionManager.class);
- ServerTransportSecurityValidator validator = ServerTransportSecurityValidatorFactory.create(sessionManager, "127.0.0.1", DISABLED_SESSION_ATTRIBUTION_RESOLVER);
- ServerTransportSecurityException ex = assertThrows(ServerTransportSecurityException.class,
- () -> validator.validateHeaders(Map.of("Origin", List.of("http://example.com:8080"), "Mcp-Session-Id", List.of("session-id"))));
- assertThat(ex.getStatusCode(), is(403));
- verifyNoInteractions(sessionManager);
- }
-
- @Test
- void assertCreateWithNonLoopbackBindingAcceptsMissingOrigin() {
- MCPSessionManager sessionManager = mock(MCPSessionManager.class);
- ServerTransportSecurityValidator actual = ServerTransportSecurityValidatorFactory.create(sessionManager, "0.0.0.0", DISABLED_SESSION_ATTRIBUTION_RESOLVER);
- assertDoesNotThrow(() -> actual.validateHeaders(Map.of()));
- verifyNoInteractions(sessionManager);
- }
-
- @Test
- void assertCreateWithNonLoopbackBindingRejectsPresentOrigin() {
- MCPSessionManager sessionManager = mock(MCPSessionManager.class);
- ServerTransportSecurityValidator validator = ServerTransportSecurityValidatorFactory.create(sessionManager, "0.0.0.0", DISABLED_SESSION_ATTRIBUTION_RESOLVER);
- ServerTransportSecurityException ex = assertThrows(ServerTransportSecurityException.class,
- () -> validator.validateHeaders(Map.of("Origin", List.of("https://gateway.example.test"))));
- assertThat(ex.getStatusCode(), is(403));
- verifyNoInteractions(sessionManager);
- }
-
- @Test
- void assertCreateRejectsInvalidOrigin() {
- MCPSessionManager sessionManager = mock(MCPSessionManager.class);
- ServerTransportSecurityValidator validator = ServerTransportSecurityValidatorFactory.create(sessionManager, "127.0.0.1", DISABLED_SESSION_ATTRIBUTION_RESOLVER);
- ServerTransportSecurityException ex = assertThrows(ServerTransportSecurityException.class, () -> validator.validateHeaders(Map.of("Origin", List.of("://bad-origin"))));
- assertThat(ex.getStatusCode(), is(403));
- verifyNoInteractions(sessionManager);
- }
-
- @Test
- void assertCreateWithProtocolVersionConstraintLast() {
- MCPSessionManager sessionManager = mock(MCPSessionManager.class);
- when(sessionManager.hasSession("session-id")).thenReturn(true);
- ServerTransportSecurityValidator validator = ServerTransportSecurityValidatorFactory.create(sessionManager, "127.0.0.1", DISABLED_SESSION_ATTRIBUTION_RESOLVER);
- ServerTransportSecurityException ex = assertThrows(ServerTransportSecurityException.class,
- () -> validator.validateHeaders(Map.of("Mcp-Session-Id", List.of("session-id"))));
- assertThat(ex.getStatusCode(), is(400));
- }
-
- @Test
- void assertCreateWithUnknownSessionSkipsProtocolVersionConstraint() {
- MCPSessionManager sessionManager = mock(MCPSessionManager.class);
- ServerTransportSecurityValidator actual = ServerTransportSecurityValidatorFactory.create(sessionManager, "127.0.0.1", DISABLED_SESSION_ATTRIBUTION_RESOLVER);
- assertDoesNotThrow(() -> actual.validateHeaders(Map.of("Mcp-Session-Id", List.of("unknown-session"))));
- }
-}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreHandlerProvider.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreHandlerProvider.java
index 385ab5fcfee..bcebc62077d 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreHandlerProvider.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreHandlerProvider.java
@@ -23,6 +23,14 @@ import org.apache.shardingsphere.mcp.api.MCPHandlerProvider;
import org.apache.shardingsphere.mcp.api.capability.tool.MCPToolHandler;
import org.apache.shardingsphere.mcp.core.completion.handler.MetadataCompletionHandler;
import org.apache.shardingsphere.mcp.core.completion.handler.WorkflowPlanIdCompletionHandler;
+import org.apache.shardingsphere.mcp.core.tool.handler.execute.ExecuteExplainToolHandler;
+import org.apache.shardingsphere.mcp.core.tool.handler.execute.ExecuteQueryToolHandler;
+import org.apache.shardingsphere.mcp.core.tool.handler.execute.ExecuteUpdateToolHandler;
+import org.apache.shardingsphere.mcp.core.tool.handler.metadata.SearchMetadataToolHandler;
+import org.apache.shardingsphere.mcp.core.tool.handler.metadata.ValidateRuntimeDatabaseToolHandler;
+import org.apache.shardingsphere.mcp.core.tool.handler.workflow.WorkflowExecutionToolHandler;
+import org.apache.shardingsphere.mcp.core.tool.handler.workflow.WorkflowValidationToolHandler;
+import org.apache.shardingsphere.mcp.core.workflow.WorkflowRuntimeDefinitionRegistry;
import java.util.Collection;
import java.util.List;
@@ -39,7 +47,15 @@ public final class CoreHandlerProvider implements MCPHandlerProvider {
@Override
public Collection<MCPToolHandler<?>> getToolHandlers() {
- return CoreToolHandlers.createHandlers();
+ WorkflowRuntimeDefinitionRegistry workflowRuntimeDefinitionRegistry = WorkflowRuntimeDefinitionRegistry.load();
+ return List.of(
+ new SearchMetadataToolHandler(),
+ new ValidateRuntimeDatabaseToolHandler(),
+ new ExecuteQueryToolHandler(),
+ new ExecuteExplainToolHandler(),
+ new ExecuteUpdateToolHandler(),
+ new WorkflowExecutionToolHandler(workflowRuntimeDefinitionRegistry),
+ new WorkflowValidationToolHandler(workflowRuntimeDefinitionRegistry));
}
@Override
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreToolHandlers.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreToolHandlers.java
deleted file mode 100644
index 9e1b14712be..00000000000
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/handler/core/CoreToolHandlers.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.core.handler.core;
-
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.mcp.api.capability.tool.MCPToolHandler;
-import org.apache.shardingsphere.mcp.core.tool.handler.execute.ExecuteExplainToolHandler;
-import org.apache.shardingsphere.mcp.core.tool.handler.execute.ExecuteQueryToolHandler;
-import org.apache.shardingsphere.mcp.core.tool.handler.execute.ExecuteUpdateToolHandler;
-import org.apache.shardingsphere.mcp.core.tool.handler.metadata.SearchMetadataToolHandler;
-import org.apache.shardingsphere.mcp.core.tool.handler.metadata.ValidateRuntimeDatabaseToolHandler;
-import org.apache.shardingsphere.mcp.core.tool.handler.workflow.WorkflowExecutionToolHandler;
-import org.apache.shardingsphere.mcp.core.tool.handler.workflow.WorkflowValidationToolHandler;
-import org.apache.shardingsphere.mcp.core.workflow.WorkflowRuntimeDefinitionRegistry;
-
-import java.util.Collection;
-import java.util.List;
-
-/**
- * Core tool handlers.
- */
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-final class CoreToolHandlers {
-
- static Collection<MCPToolHandler<?>> createHandlers() {
- WorkflowRuntimeDefinitionRegistry workflowRuntimeDefinitionRegistry = WorkflowRuntimeDefinitionRegistry.load();
- return List.of(
- new SearchMetadataToolHandler(),
- new ValidateRuntimeDatabaseToolHandler(),
- new ExecuteQueryToolHandler(),
- new ExecuteExplainToolHandler(),
- new ExecuteUpdateToolHandler(),
- new WorkflowExecutionToolHandler(workflowRuntimeDefinitionRegistry),
- new WorkflowValidationToolHandler(workflowRuntimeDefinitionRegistry));
- }
-}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/MCPResourceDefinition.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/MCPResourceDefinition.java
deleted file mode 100644
index e65c5f3c5a7..00000000000
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/MCPResourceDefinition.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.core.resource.handler;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.mcp.api.capability.resource.MCPResourceHandler;
-import org.apache.shardingsphere.mcp.api.capability.resource.MCPResourceDescriptor;
-import org.apache.shardingsphere.mcp.core.resource.uri.MCPUriPattern;
-
-/**
- * MCP resource definition.
- */
-@RequiredArgsConstructor
-@Getter
-final class MCPResourceDefinition {
-
- private final MCPUriPattern uriPattern;
-
- private final MCPResourceDescriptor descriptor;
-
- private final MCPResourceHandler<?> handler;
-}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/ResourceDefinitionRegistry.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/ResourceDefinitionRegistry.java
index eb23625b573..14213fb6c35 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/ResourceDefinitionRegistry.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/ResourceDefinitionRegistry.java
@@ -47,7 +47,7 @@ import java.util.Optional;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ResourceDefinitionRegistry {
- private static final List<MCPResourceDefinition> REGISTERED_RESOURCE_DEFINITIONS;
+ private static final List<ResourceDefinition> REGISTERED_RESOURCE_DEFINITIONS;
static {
Map<MCPUriPattern, MCPResourceHandler<?>> resourceHandlers = createRegisteredResourceHandlers(
@@ -91,12 +91,9 @@ public final class ResourceDefinitionRegistry {
}
}
- private static List<MCPResourceDefinition> createRegisteredResourceDefinitions(final Map<MCPUriPattern, MCPResourceHandler<?>> handlers) {
- return handlers.entrySet().stream().map(entry -> createResourceDefinition(entry.getKey(), entry.getValue())).toList();
- }
-
- private static MCPResourceDefinition createResourceDefinition(final MCPUriPattern uriPattern, final MCPResourceHandler<?> handler) {
- return new MCPResourceDefinition(uriPattern, MCPDescriptorCatalogIndex.getRequiredResourceDescriptor(handler.getResourceUriTemplate()), handler);
+ private static List<ResourceDefinition> createRegisteredResourceDefinitions(final Map<MCPUriPattern, MCPResourceHandler<?>> handlers) {
+ return handlers.entrySet().stream().map(entry -> new ResourceDefinition(entry.getKey(),
+ MCPDescriptorCatalogIndex.getRequiredResourceDescriptor(entry.getValue().getResourceUriTemplate()), entry.getValue())).toList();
}
private static void validateRegisteredResourceDescriptors() {
@@ -107,8 +104,8 @@ public final class ResourceDefinitionRegistry {
}
private static boolean isRegisteredResourceDescriptor(final MCPResourceDescriptor descriptor) {
- for (MCPResourceDefinition each : REGISTERED_RESOURCE_DEFINITIONS) {
- if (descriptor.getUriTemplate().equals(each.getUriPattern().getPattern())) {
+ for (ResourceDefinition each : REGISTERED_RESOURCE_DEFINITIONS) {
+ if (descriptor.getUriTemplate().equals(each.uriPattern().getPattern())) {
return true;
}
}
@@ -123,19 +120,15 @@ public final class ResourceDefinitionRegistry {
* @return handled payload
*/
public static Optional<MCPSuccessPayload> dispatch(final MCPFeatureRuntimeRequestContext requestContext, final String resourceUri) {
- for (MCPResourceDefinition each : REGISTERED_RESOURCE_DEFINITIONS) {
- Optional<MCPResourceURIVariables> matchedUriVariables = each.getUriPattern().parse(resourceUri);
+ for (ResourceDefinition each : REGISTERED_RESOURCE_DEFINITIONS) {
+ Optional<MCPResourceURIVariables> matchedUriVariables = each.uriPattern().parse(resourceUri);
if (matchedUriVariables.isPresent()) {
- return Optional.of(dispatch(requestContext, each, matchedUriVariables.get()));
+ return Optional.of(dispatch(requestContext, each.handler(), matchedUriVariables.get()));
}
}
return Optional.empty();
}
- private static MCPSuccessPayload dispatch(final MCPFeatureRuntimeRequestContext requestContext, final MCPResourceDefinition resourceDefinition, final MCPResourceURIVariables uriVariables) {
- return dispatch(requestContext, resourceDefinition.getHandler(), uriVariables);
- }
-
private static <T extends MCPRequestContext> MCPSuccessPayload dispatch(final MCPFeatureRuntimeRequestContext requestContext, final MCPResourceHandler<T> resourceHandler,
final MCPResourceURIVariables uriVariables) {
return resourceHandler.handle(resourceHandler.getContextType().cast(requestContext), uriVariables);
@@ -147,6 +140,9 @@ public final class ResourceDefinitionRegistry {
* @return supported resource descriptors
*/
public static Collection<MCPResourceDescriptor> getSupportedResourceDescriptors() {
- return REGISTERED_RESOURCE_DEFINITIONS.stream().map(MCPResourceDefinition::getDescriptor).toList();
+ return REGISTERED_RESOURCE_DEFINITIONS.stream().map(ResourceDefinition::descriptor).toList();
+ }
+
+ private record ResourceDefinition(MCPUriPattern uriPattern, MCPResourceDescriptor descriptor, MCPResourceHandler<?> handler) {
}
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowExecutionToolHandler.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowExecutionToolHandler.java
index c5f2685f3c1..360733132eb 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowExecutionToolHandler.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowExecutionToolHandler.java
@@ -73,7 +73,7 @@ public final class WorkflowExecutionToolHandler implements MCPToolHandler<MCPFea
WorkflowContextSnapshot snapshot = workflowSessionContext.getRequired(toolArguments.getStringArgument(WorkflowFieldNames.PLAN_ID));
WorkflowRuntimeDefinition workflowRuntimeDefinition = workflowRuntimeDefinitionRegistry.getRequired(snapshot);
return new MCPMapPayload(executionService.apply(workflowSessionContext, requestContext.getMetadataQueryFacade(), requestContext.getQueryFacade(),
- requestContext.getExecutionFacade(), workflowRuntimeDefinition.getApplySynchronizationHandler(), workflowRuntimeDefinition.getApplyArtifactValidator(),
+ requestContext.getExecutionFacade(), workflowRuntimeDefinition.getRuntimeHandler(), workflowRuntimeDefinition.getApplyArtifactValidator(),
requestContext.getSessionIdentity().getSessionId(),
snapshot, toolArguments.getStringCollectionArgument(WorkflowFieldNames.APPROVED_STEPS), executionMode));
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowValidationToolHandler.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowValidationToolHandler.java
index da5ebc5d346..1090629680b 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowValidationToolHandler.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowValidationToolHandler.java
@@ -54,7 +54,7 @@ public final class WorkflowValidationToolHandler implements MCPToolHandler<MCPFe
MCPToolArguments toolArguments = new MCPToolArguments(arguments);
WorkflowSessionContext workflowSessionContext = requestContext.getWorkflowSessionContext();
WorkflowContextSnapshot snapshot = workflowSessionContext.getRequired(toolArguments.getStringArgument(WorkflowFieldNames.PLAN_ID));
- return new MCPMapPayload(workflowRuntimeDefinitionRegistry.getRequired(snapshot).getValidationHandler().validate(workflowSessionContext,
+ return new MCPMapPayload(workflowRuntimeDefinitionRegistry.getRequired(snapshot).getRuntimeHandler().validate(workflowSessionContext,
requestContext.getMetadataQueryFacade(), requestContext.getQueryFacade(), requestContext.getExecutionFacade(), requestContext.getSessionIdentity().getSessionId(), snapshot));
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowApplyOutcome.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowApplyOutcome.java
index b371565cffa..8072e46dffc 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowApplyOutcome.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowApplyOutcome.java
@@ -32,7 +32,7 @@ import java.util.Map;
/**
* Mutable outcome collected while applying workflow artifacts.
*/
-public final class WorkflowApplyOutcome {
+final class WorkflowApplyOutcome {
private final List<Map<String, Object>> stepResults = new LinkedList<>();
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionService.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionService.java
index fece5edf28c..1079e188e8f 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionService.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionService.java
@@ -28,7 +28,6 @@ import org.apache.shardingsphere.mcp.support.database.tool.request.SQLExecutionR
import org.apache.shardingsphere.mcp.support.protocol.MCPNextActionUtils;
import org.apache.shardingsphere.mcp.support.protocol.MCPPayloadFieldNames;
import org.apache.shardingsphere.mcp.support.protocol.MCPResponseMode;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.WorkflowSessionContext;
import org.apache.shardingsphere.mcp.support.workflow.descriptor.WorkflowToolDescriptors;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
@@ -36,6 +35,7 @@ import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowIssue;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowIssueCode;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowLifecycle;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowArtifactBundle;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowArtifactMaskUtils;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowArtifactPayloadUtils;
@@ -44,7 +44,7 @@ import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowLifecycleU
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowSecretReferenceUtils;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowSynchronizationException;
import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplyArtifactValidator;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplySynchronizationHandler;
+import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowRuntimeHandler;
import java.util.List;
import java.util.Locale;
@@ -67,7 +67,7 @@ public final class WorkflowExecutionService {
* @param metadataQueryFacade metadata query facade
* @param queryFacade query facade
* @param executionFacade execution facade
- * @param workflowApplySynchronizationHandler workflow apply synchronization handler
+ * @param workflowRuntimeHandler workflow runtime handler
* @param workflowApplyArtifactValidator workflow apply artifact validator
* @param sessionId session id
* @param snapshot workflow snapshot
@@ -76,7 +76,7 @@ public final class WorkflowExecutionService {
* @return apply payload
*/
public Map<String, Object> apply(final WorkflowSessionContext workflowSessionContext, final MCPMetadataQueryFacade metadataQueryFacade, final MCPFeatureQueryFacade queryFacade,
- final MCPFeatureExecutionFacade executionFacade, final MCPWorkflowApplySynchronizationHandler workflowApplySynchronizationHandler,
+ final MCPFeatureExecutionFacade executionFacade, final MCPWorkflowRuntimeHandler workflowRuntimeHandler,
final MCPWorkflowApplyArtifactValidator workflowApplyArtifactValidator, final String sessionId, final WorkflowContextSnapshot snapshot,
final List<String> approvedSteps, final String executionMode) {
String actualExecutionMode = requireExecutionMode(snapshot, executionMode);
@@ -96,7 +96,7 @@ public final class WorkflowExecutionService {
if (isManualOnly(actualExecutionMode)) {
return applyManualOnly(workflowSessionContext, snapshot, applyOutcome);
}
- return applyAutomatically(workflowSessionContext, metadataQueryFacade, queryFacade, executionFacade, workflowApplySynchronizationHandler, sessionId, snapshot,
+ return applyAutomatically(workflowSessionContext, metadataQueryFacade, queryFacade, executionFacade, workflowRuntimeHandler, sessionId, snapshot,
actualExecutionMode, applyOutcome);
}
@@ -225,10 +225,10 @@ public final class WorkflowExecutionService {
private Map<String, Object> applyAutomatically(final WorkflowSessionContext workflowSessionContext, final MCPMetadataQueryFacade metadataQueryFacade,
final MCPFeatureQueryFacade queryFacade, final MCPFeatureExecutionFacade executionFacade,
- final MCPWorkflowApplySynchronizationHandler workflowApplySynchronizationHandler,
+ final MCPWorkflowRuntimeHandler workflowRuntimeHandler,
final String sessionId, final WorkflowContextSnapshot snapshot, final String executionMode,
final WorkflowApplyOutcome applyOutcome) {
- if (WorkflowSecretReferenceUtils.hasSecretReferences(getPropertySource(snapshot))) {
+ if (WorkflowSecretReferenceUtils.hasSecretReferences(getRequest(snapshot))) {
return failSecretReferenceManualExecutionRequired(workflowSessionContext, snapshot, executionMode, applyOutcome);
}
String currentArtifactDisplaySql = "";
@@ -238,7 +238,7 @@ public final class WorkflowExecutionService {
executeArtifact(executionFacade, sessionId, snapshot, each);
applyOutcome.addExecutedArtifact(each);
}
- workflowApplySynchronizationHandler.synchronize(snapshot, metadataQueryFacade, queryFacade, executionFacade, sessionId);
+ workflowRuntimeHandler.synchronize(snapshot, metadataQueryFacade, queryFacade, executionFacade, sessionId);
return completeApply(workflowSessionContext, snapshot, executionMode, applyOutcome);
} catch (final WorkflowSynchronizationException ex) {
return failApplySynchronization(workflowSessionContext, snapshot, executionMode, applyOutcome, ex);
@@ -259,15 +259,15 @@ public final class WorkflowExecutionService {
final WorkflowApplyOutcome applyOutcome, final String artifactSql, final RuntimeException ex) {
persistSnapshot(workflowSessionContext, snapshot, WorkflowLifecycle.STEP_FAILED, WorkflowLifecycle.STATUS_FAILED);
applyOutcome.addFailedArtifact(artifactSql, WorkflowArtifactMaskUtils.maskSensitiveSql(
- null == ex.getMessage() ? "" : ex.getMessage(), getPropertySource(snapshot), snapshot.getPropertyRequirements()));
+ null == ex.getMessage() ? "" : ex.getMessage(), getRequest(snapshot), snapshot.getPropertyRequirements()));
return applyOutcome.createResponse(WorkflowLifecycle.STATUS_FAILED, snapshot, executionMode, Map.of());
}
private Map<String, Object> failSecretReferenceManualExecutionRequired(final WorkflowSessionContext workflowSessionContext, final WorkflowContextSnapshot snapshot,
final String executionMode, final WorkflowApplyOutcome applyOutcome) {
persistSnapshot(workflowSessionContext, snapshot, WorkflowLifecycle.STEP_FAILED, WorkflowLifecycle.STATUS_FAILED);
- WorkflowPropertySource propertySource = getPropertySource(snapshot);
- String category = WorkflowSecretReferenceUtils.hasMalformedSecretReferences(propertySource)
+ WorkflowRequest request = getRequest(snapshot);
+ String category = WorkflowSecretReferenceUtils.hasMalformedSecretReferences(request)
? MCPDiagnosticCategory.SECRET_REFERENCE_MALFORMED
: MCPDiagnosticCategory.SECRET_REFERENCE_MANUAL_EXECUTION_REQUIRED;
applyOutcome.addSecretReferenceManualExecutionRequired();
@@ -275,7 +275,7 @@ public final class WorkflowExecutionService {
result.put("response_mode", MCPResponseMode.RECOVERY);
result.put("category", category);
result.put(MCPPayloadFieldNames.SUMMARY, "This workflow contains sensitive placeholders that must be filled outside MCP before execution.");
- result.put("secret_reference_summary", WorkflowArtifactMaskUtils.createSecretReferenceSummary(propertySource));
+ result.put("secret_reference_summary", WorkflowArtifactMaskUtils.createSecretReferenceSummary(request));
WorkflowGuidancePayloadBuilder.appendApplyGuidance(result, WorkflowLifecycle.STATUS_FAILED);
return result;
}
@@ -295,18 +295,17 @@ public final class WorkflowExecutionService {
workflowSessionContext.save(snapshot);
}
- private WorkflowPropertySource getPropertySource(final WorkflowContextSnapshot snapshot) {
- return null == snapshot.getRequest() ? algorithmRole -> Map.of() : snapshot.getRequest();
+ private WorkflowRequest getRequest(final WorkflowContextSnapshot snapshot) {
+ return null == snapshot.getRequest() ? new WorkflowRequest() : snapshot.getRequest();
}
private Map<String, Object> createArtifactPayload(final WorkflowContextSnapshot snapshot) {
- return WorkflowArtifactPayloadUtils.createArtifactPayload(snapshot, getPropertySource(snapshot));
+ return WorkflowArtifactPayloadUtils.createArtifactPayload(snapshot, getRequest(snapshot));
}
private List<WorkflowArtifactBundle.ExecutableWorkflowArtifact> createExecutableArtifacts(final WorkflowContextSnapshot snapshot) {
WorkflowArtifactBundle result = WorkflowArtifactBundle.from(snapshot);
- WorkflowPropertySource propertySource = getPropertySource(snapshot);
- return result.toExecutableArtifacts(propertySource, snapshot.getPropertyRequirements());
+ return result.toExecutableArtifacts(getRequest(snapshot), snapshot.getPropertyRequirements());
}
private void executeArtifact(final MCPFeatureExecutionFacade executionFacade, final String sessionId, final WorkflowContextSnapshot snapshot,
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 e1a0953d5c8..3af4c9ca9e2 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
@@ -51,10 +51,8 @@ public final class WorkflowRuntimeDefinitionRegistry {
Objects.requireNonNull(each, "Workflow definition is required.");
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())));
+ ShardingSpherePreconditions.checkNotNull(each.getRuntimeHandler(),
+ () -> new IllegalArgumentException(String.format("Workflow runtime handler is required for `%s`.", workflowKind.getValue())));
ShardingSpherePreconditions.checkNotNull(each.getApplyArtifactValidator(),
() -> new IllegalArgumentException(String.format("Workflow apply artifact validator is required for `%s`.", workflowKind.getValue())));
WorkflowRuntimeDefinition previousDefinition = result.putIfAbsent(workflowKind, each);
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/handler/MetadataCompletionHandlerTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/handler/MetadataCompletionHandlerTest.java
index 3a5b2cdd175..5a78d27709e 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/handler/MetadataCompletionHandlerTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/handler/MetadataCompletionHandlerTest.java
@@ -55,11 +55,6 @@ import static org.mockito.Mockito.when;
class MetadataCompletionHandlerTest {
- @Test
- void assertGetContextType() {
- assertThat(new MetadataCompletionHandler().getContextType(), is(MCPFeatureRequestContext.class));
- }
-
@Test
void assertSupports() {
assertTrue(new MetadataCompletionHandler().supports(createRequestContext("database", Map.of())));
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/handler/WorkflowPlanIdCompletionHandlerTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/handler/WorkflowPlanIdCompletionHandlerTest.java
index 2794fb4e337..7210591deea 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/handler/WorkflowPlanIdCompletionHandlerTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/handler/WorkflowPlanIdCompletionHandlerTest.java
@@ -41,11 +41,6 @@ import static org.mockito.Mockito.when;
class WorkflowPlanIdCompletionHandlerTest {
- @Test
- void assertGetContextType() {
- assertThat(new WorkflowPlanIdCompletionHandler().getContextType(), is(MCPFeatureRequestContext.class));
- }
-
@Test
void assertSupports() {
assertTrue(new WorkflowPlanIdCompletionHandler().supports(createRequestContext()));
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/ServerCapabilitiesHandlerTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/ServerCapabilitiesHandlerTest.java
index ded8794e8b8..cc98e0a14ed 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/ServerCapabilitiesHandlerTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/ServerCapabilitiesHandlerTest.java
@@ -137,11 +137,7 @@ class ServerCapabilitiesHandlerTest {
assertTrue(actual.containsKey("http_transport"));
assertTrue(actual.containsKey("origin_header"));
assertTrue(actual.containsKey("stdio_stdout"));
- Map<?, ?> actualClientSafetyPolicy = (Map<?, ?>) actual.get("client_safety_policy");
- assertThat(actualClientSafetyPolicy.get("identity_scope"), is("mcp_session"));
- assertTrue(String.valueOf(actualClientSafetyPolicy.get("transport_scope")).contains("trusted session attribution"));
- assertThat(((Map<?, ?>) ((Map<?, ?>) actualClientSafetyPolicy.get("runtime_protection")).get("tool_call_limit")).get("scope"), is("session"));
- assertTrue(String.valueOf(actualClientSafetyPolicy.get("abuse_guard")).contains("counted before dispatch"));
+ assertTrue(actual.containsKey("client_safety_policy"));
}
private Map<?, ?> findByKey(final List<?> values, final String key, final String expectedValue) {
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
index ebc15274fc0..4e4c2cc11a3 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
@@ -21,12 +21,10 @@ import org.apache.shardingsphere.database.connector.core.metadata.identifier.Ide
import org.apache.shardingsphere.infra.metadata.identifier.DatabaseIdentifierContext;
import org.apache.shardingsphere.mcp.api.payload.MCPSuccessPayload;
import org.apache.shardingsphere.mcp.api.capability.resource.MCPResourceURIVariables;
-import org.apache.shardingsphere.mcp.api.capability.resource.MCPResourceDescriptor;
import org.apache.shardingsphere.mcp.support.MCPFeatureRequestContext;
import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseProfile;
import org.apache.shardingsphere.mcp.support.database.metadata.TransactionCapability;
import org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureCapabilityFacade;
-import org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalogIndex;
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -41,14 +39,6 @@ import static org.mockito.Mockito.when;
class MetadataResourceHandlerTest {
- @Test
- void assertGetResourceDescriptor() {
- MetadataResourceHandler handler = new MetadataResourceHandler("shardingsphere://databases", (requestContext, uriVariables) -> List.of());
- MCPResourceDescriptor actual = MCPDescriptorCatalogIndex.getRequiredResourceDescriptor(handler.getResourceUriTemplate());
- assertThat(actual.getUriTemplate(), is("shardingsphere://databases"));
- assertThat(actual.getTitle(), is("Logical Databases"));
- }
-
@Test
void assertHandleListResource() {
MetadataResourceHandler handler = new MetadataResourceHandler("shardingsphere://databases",
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/uri/MCPUriPatternTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/uri/MCPUriPatternTest.java
index 21b619e8760..4b1a64dadf9 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/uri/MCPUriPatternTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/uri/MCPUriPatternTest.java
@@ -30,12 +30,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class MCPUriPatternTest {
- @Test
- void assertGetPattern() {
- MCPUriPattern actual = new MCPUriPattern("shardingsphere://capabilities");
- assertThat(actual.getPattern(), is("shardingsphere://capabilities"));
- }
-
@Test
void assertCreateWithUnsupportedScheme() {
assertThrows(IllegalArgumentException.class, () -> new MCPUriPattern("unsupported://capabilities"));
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowExecutionToolHandlerTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowExecutionToolHandlerTest.java
index ad0b1206aec..2ea0843d0bf 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowExecutionToolHandlerTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowExecutionToolHandlerTest.java
@@ -25,8 +25,7 @@ import org.apache.shardingsphere.mcp.core.workflow.WorkflowRuntimeDefinitionRegi
import org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalogIndex;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplyArtifactValidator;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplySynchronizationHandler;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowValidationHandler;
+import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowRuntimeHandler;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
@@ -57,18 +56,18 @@ class WorkflowExecutionToolHandlerTest {
void assertHandleExecution() {
WorkflowContextSnapshot snapshot = WorkflowHandlerTestFixture.createSnapshot();
WorkflowHandlerTestFixture.Context fixture = WorkflowHandlerTestFixture.createContext(snapshot);
- MCPWorkflowApplySynchronizationHandler workflowApplySynchronizationHandler = mock(MCPWorkflowApplySynchronizationHandler.class);
+ MCPWorkflowRuntimeHandler workflowRuntimeHandler = mock(MCPWorkflowRuntimeHandler.class);
MCPWorkflowApplyArtifactValidator workflowApplyArtifactValidator = mock(MCPWorkflowApplyArtifactValidator.class);
try (
MockedConstruction<WorkflowExecutionService> mockedExecutionServices = mockConstruction(WorkflowExecutionService.class,
(mock, context) -> when(mock.apply(any(), any(), any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(Map.of("status", "completed")))) {
WorkflowExecutionToolHandler handler = new WorkflowExecutionToolHandler(new WorkflowRuntimeDefinitionRegistry(List.of(
- WorkflowHandlerTestFixture.createDefinition("encrypt.rule", mock(MCPWorkflowValidationHandler.class), workflowApplySynchronizationHandler, workflowApplyArtifactValidator))));
+ WorkflowHandlerTestFixture.createDefinition("encrypt.rule", workflowRuntimeHandler, workflowApplyArtifactValidator))));
MCPSuccessPayload actual = handler.handle(fixture.requestContext(), Map.of(WorkflowFieldNames.PLAN_ID, "plan-1",
WorkflowFieldNames.APPROVED_STEPS, List.of("rule_distsql"), WorkflowFieldNames.EXECUTION_MODE, "manual-only"));
WorkflowExecutionService executionService = mockedExecutionServices.constructed().getFirst();
verify(executionService).apply(eq(fixture.workflowSessionContext()), eq(fixture.metadataQueryFacade()), eq(fixture.queryFacade()), eq(fixture.executionFacade()),
- eq(workflowApplySynchronizationHandler), eq(workflowApplyArtifactValidator), eq("session-1"), eq(snapshot), eq(List.of("rule_distsql")), eq("manual-only"));
+ eq(workflowRuntimeHandler), eq(workflowApplyArtifactValidator), eq("session-1"), eq(snapshot), eq(List.of("rule_distsql")), eq("manual-only"));
assertThat(actual.toPayload().get("status"), is("completed"));
}
}
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowHandlerTestFixture.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowHandlerTestFixture.java
index 7089a5120db..c9e015c126e 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowHandlerTestFixture.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowHandlerTestFixture.java
@@ -28,8 +28,7 @@ import org.apache.shardingsphere.mcp.support.workflow.WorkflowSessionContext;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowKind;
import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplyArtifactValidator;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplySynchronizationHandler;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowValidationHandler;
+import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowRuntimeHandler;
import org.apache.shardingsphere.mcp.support.workflow.spi.WorkflowRuntimeDefinition;
import java.util.Map;
@@ -69,20 +68,12 @@ final class WorkflowHandlerTestFixture {
}
static WorkflowRuntimeDefinition createDefinition(final String workflowKind) {
- return new WorkflowRuntimeDefinition(WorkflowKind.valueOf(workflowKind), (workflowSessionContext, metadataQueryFacade, queryFacade, executionFacade, sessionId, snapshot) -> Map.of(),
- (snapshot, metadataQueryFacade, queryFacade, executionFacade, sessionId) -> {
- }, MCPWorkflowApplyArtifactValidator.NO_OP);
+ return new WorkflowRuntimeDefinition(WorkflowKind.valueOf(workflowKind), mock(MCPWorkflowRuntimeHandler.class));
}
- static WorkflowRuntimeDefinition createDefinition(final String workflowKind, final MCPWorkflowValidationHandler validationHandler,
- final MCPWorkflowApplySynchronizationHandler applySynchronizationHandler) {
- return new WorkflowRuntimeDefinition(WorkflowKind.valueOf(workflowKind), validationHandler, applySynchronizationHandler, MCPWorkflowApplyArtifactValidator.NO_OP);
- }
-
- static WorkflowRuntimeDefinition createDefinition(final String workflowKind, final MCPWorkflowValidationHandler validationHandler,
- final MCPWorkflowApplySynchronizationHandler applySynchronizationHandler,
+ static WorkflowRuntimeDefinition createDefinition(final String workflowKind, final MCPWorkflowRuntimeHandler runtimeHandler,
final MCPWorkflowApplyArtifactValidator applyArtifactValidator) {
- return new WorkflowRuntimeDefinition(WorkflowKind.valueOf(workflowKind), validationHandler, applySynchronizationHandler, applyArtifactValidator);
+ return new WorkflowRuntimeDefinition(WorkflowKind.valueOf(workflowKind), runtimeHandler, applyArtifactValidator);
}
record Context(MCPFeatureRequestContext requestContext, WorkflowSessionContext workflowSessionContext,
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowValidationToolHandlerTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowValidationToolHandlerTest.java
index 21547a0341b..b715cf26a4c 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowValidationToolHandlerTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/workflow/WorkflowValidationToolHandlerTest.java
@@ -22,8 +22,8 @@ import org.apache.shardingsphere.mcp.api.capability.tool.MCPToolDescriptor;
import org.apache.shardingsphere.mcp.core.workflow.WorkflowRuntimeDefinitionRegistry;
import org.apache.shardingsphere.mcp.support.descriptor.MCPDescriptorCatalogIndex;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplySynchronizationHandler;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowValidationHandler;
+import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplyArtifactValidator;
+import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowRuntimeHandler;
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -48,14 +48,14 @@ class WorkflowValidationToolHandlerTest {
@Test
void assertHandle() {
- MCPWorkflowValidationHandler workflowValidationHandler = mock(MCPWorkflowValidationHandler.class);
- when(workflowValidationHandler.validate(any(), any(), any(), any(), any(), any())).thenReturn(Map.of("status", "validated"));
+ MCPWorkflowRuntimeHandler workflowRuntimeHandler = mock(MCPWorkflowRuntimeHandler.class);
+ when(workflowRuntimeHandler.validate(any(), any(), any(), any(), any(), any())).thenReturn(Map.of("status", "validated"));
WorkflowContextSnapshot snapshot = WorkflowHandlerTestFixture.createSnapshot();
WorkflowHandlerTestFixture.Context fixture = WorkflowHandlerTestFixture.createContext(snapshot);
WorkflowValidationToolHandler handler = new WorkflowValidationToolHandler(new WorkflowRuntimeDefinitionRegistry(List.of(
- WorkflowHandlerTestFixture.createDefinition("encrypt.rule", workflowValidationHandler, mock(MCPWorkflowApplySynchronizationHandler.class)))));
+ WorkflowHandlerTestFixture.createDefinition("encrypt.rule", workflowRuntimeHandler, MCPWorkflowApplyArtifactValidator.NO_OP))));
MCPSuccessPayload actual = handler.handle(fixture.requestContext(), Map.of("plan_id", "plan-1"));
- verify(workflowValidationHandler).validate(eq(fixture.workflowSessionContext()), eq(fixture.metadataQueryFacade()),
+ verify(workflowRuntimeHandler).validate(eq(fixture.workflowSessionContext()), eq(fixture.metadataQueryFacade()),
eq(fixture.queryFacade()), eq(fixture.executionFacade()), eq("session-1"), eq(snapshot));
assertThat(actual.toPayload().get("status"), is("validated"));
}
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionServiceTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionServiceTest.java
index cb110192ae8..86b25bc51f7 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionServiceTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowExecutionServiceTest.java
@@ -35,7 +35,7 @@ import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowKind;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowSynchronizationException;
import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplyArtifactValidator;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplySynchronizationHandler;
+import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowRuntimeHandler;
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -56,9 +56,20 @@ import static org.mockito.Mockito.when;
class WorkflowExecutionServiceTest {
- private static final MCPWorkflowApplySynchronizationHandler NO_OP_APPLY_SYNCHRONIZATION_HANDLER =
- (snapshot, metadataQueryFacade, queryFacade, executionFacade, sessionId) -> {
- };
+ private static final MCPWorkflowRuntimeHandler NO_OP_RUNTIME_HANDLER = new MCPWorkflowRuntimeHandler() {
+
+ @Override
+ public Map<String, Object> validate(final WorkflowSessionContext workflowSessionContext, final MCPMetadataQueryFacade metadataQueryFacade,
+ final MCPFeatureQueryFacade queryFacade, final MCPFeatureExecutionFacade executionFacade,
+ final String sessionId, final WorkflowContextSnapshot snapshot) {
+ return Map.of();
+ }
+
+ @Override
+ public void synchronize(final WorkflowContextSnapshot snapshot, final MCPMetadataQueryFacade metadataQueryFacade,
+ final MCPFeatureQueryFacade queryFacade, final MCPFeatureExecutionFacade executionFacade, final String sessionId) {
+ }
+ };
@Test
void assertApplyMasksManualArtifactPackage() {
@@ -91,7 +102,7 @@ class WorkflowExecutionServiceTest {
workflowSessionContext.save(createSnapshot());
WorkflowExecutionService executionService = new WorkflowExecutionService();
Map<String, Object> actualResponse = executionService.apply(workflowSessionContext, mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class),
- mock(MCPFeatureExecutionFacade.class), NO_OP_APPLY_SYNCHRONIZATION_HANDLER, MCPWorkflowApplyArtifactValidator.NO_OP,
+ mock(MCPFeatureExecutionFacade.class), NO_OP_RUNTIME_HANDLER, MCPWorkflowApplyArtifactValidator.NO_OP,
"session-2", workflowSessionContext.getRequired("plan-1"), List.of(), "review-then-execute");
assertThat(actualResponse.get("status"), is("failed"));
assertThat(actualResponse.get("response_mode"), is("terminal"));
@@ -117,7 +128,7 @@ class WorkflowExecutionServiceTest {
WorkflowExecutionService executionService = new WorkflowExecutionService();
MCPInvalidRequestException actual = assertThrows(MCPInvalidRequestException.class,
() -> executionService.apply(new InMemoryWorkflowSessionStore().getSessionContext("session-1"),
- mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class), mock(MCPFeatureExecutionFacade.class), NO_OP_APPLY_SYNCHRONIZATION_HANDLER,
+ mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class), mock(MCPFeatureExecutionFacade.class), NO_OP_RUNTIME_HANDLER,
MCPWorkflowApplyArtifactValidator.NO_OP, "session-1", createSnapshot(), List.of(), ""));
assertThat(actual.getMessage(), is("database_gateway_apply_workflow execution_mode is required."));
}
@@ -127,7 +138,7 @@ class WorkflowExecutionServiceTest {
WorkflowExecutionService executionService = new WorkflowExecutionService();
MCPInvalidRequestException actual = assertThrows(MCPInvalidRequestException.class,
() -> executionService.apply(new InMemoryWorkflowSessionStore().getSessionContext("session-1"),
- mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class), mock(MCPFeatureExecutionFacade.class), NO_OP_APPLY_SYNCHRONIZATION_HANDLER,
+ mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class), mock(MCPFeatureExecutionFacade.class), NO_OP_RUNTIME_HANDLER,
MCPWorkflowApplyArtifactValidator.NO_OP, "session-1", createSnapshot(), List.of(), "auto-execute"));
assertThat(actual.getMessage(), is("database_gateway_apply_workflow execution_mode must be one of [preview, review-then-execute, manual-only]."));
}
@@ -137,7 +148,7 @@ class WorkflowExecutionServiceTest {
WorkflowExecutionService executionService = new WorkflowExecutionService();
MCPInvalidRequestException actual = assertThrows(MCPInvalidRequestException.class,
() -> executionService.apply(new InMemoryWorkflowSessionStore().getSessionContext("session-1"),
- mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class), mock(MCPFeatureExecutionFacade.class), NO_OP_APPLY_SYNCHRONIZATION_HANDLER,
+ mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class), mock(MCPFeatureExecutionFacade.class), NO_OP_RUNTIME_HANDLER,
MCPWorkflowApplyArtifactValidator.NO_OP, "session-1", createSnapshot(), List.of("review"), "review-then-execute"));
assertThat(actual.getMessage(), is("approved_steps must contain only [rule_distsql]."));
}
@@ -149,7 +160,7 @@ class WorkflowExecutionServiceTest {
snapshot.getRuleArtifacts().add(new RuleArtifact("create", "CREATE MASK RULE orders"));
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
when(executionFacade.execute(any())).thenReturn(mock(SQLExecutionResult.class));
- Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_APPLY_SYNCHRONIZATION_HANDLER, List.of(), "review-then-execute");
+ Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_RUNTIME_HANDLER, List.of(), "review-then-execute");
assertThat(actualResponse.get("status"), is("failed"));
assertThat(((Map<?, ?>) ((List<?>) actualResponse.get("issues")).getFirst()).get("code"), is(WorkflowIssueCode.WORKFLOW_STATUS_INVALID));
verify(executionFacade, never()).execute(any());
@@ -160,7 +171,7 @@ class WorkflowExecutionServiceTest {
WorkflowContextSnapshot snapshot = createSnapshot();
snapshot.getRuleArtifacts().add(new RuleArtifact("create", "CREATE MASK RULE orders"));
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
- Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_APPLY_SYNCHRONIZATION_HANDLER, List.of("rule_distsql"), "review-then-execute");
+ Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_RUNTIME_HANDLER, List.of("rule_distsql"), "review-then-execute");
assertThat(actualResponse.get("status"), is("failed"));
assertThat(((Map<?, ?>) ((List<?>) actualResponse.get("issues")).getFirst()).get("code"), is(WorkflowIssueCode.WORKFLOW_STATUS_INVALID));
verify(executionFacade, never()).execute(any());
@@ -171,7 +182,7 @@ class WorkflowExecutionServiceTest {
WorkflowContextSnapshot snapshot = createSnapshot();
snapshot.setStatus("previewed");
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
- Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_APPLY_SYNCHRONIZATION_HANDLER,
+ Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_RUNTIME_HANDLER,
List.of("rule_distsql"), "review-then-execute");
assertThat(actualResponse.get("status"), is("failed"));
assertThat(((Map<?, ?>) ((List<?>) actualResponse.get("issues")).getFirst()).get("code"), is(WorkflowIssueCode.WORKFLOW_STATUS_INVALID));
@@ -185,8 +196,8 @@ class WorkflowExecutionServiceTest {
snapshot.getRuleArtifacts().add(new RuleArtifact("alter", "ALTER MASK RULE orders"));
WorkflowSessionContext workflowSessionContext = createWorkflowSessionContext(snapshot);
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
- MCPWorkflowApplySynchronizationHandler workflowApplySynchronizationHandler = mock(MCPWorkflowApplySynchronizationHandler.class);
- Map<String, Object> actualResponse = apply(workflowSessionContext, snapshot, executionFacade, workflowApplySynchronizationHandler, List.of(), "preview");
+ MCPWorkflowRuntimeHandler workflowRuntimeHandler = mock(MCPWorkflowRuntimeHandler.class);
+ Map<String, Object> actualResponse = apply(workflowSessionContext, snapshot, executionFacade, workflowRuntimeHandler, List.of(), "preview");
assertThat(actualResponse.get("status"), is("preview"));
assertThat(actualResponse.get("response_mode"), is("preview"));
assertFalse((boolean) actualResponse.get("would_apply"));
@@ -212,7 +223,7 @@ class WorkflowExecutionServiceTest {
assertThat(((Map<?, ?>) actualResponse.get("argument_provenance")).get("execution_mode"), is("server_defaulted"));
assertThat(workflowSessionContext.getRequired("plan-1").getStatus(), is("previewed"));
verify(executionFacade, never()).execute(any());
- verify(workflowApplySynchronizationHandler, never()).synchronize(any(), any(), any(), any(), any());
+ verify(workflowRuntimeHandler, never()).synchronize(any(), any(), any(), any(), any());
}
@Test
@@ -245,7 +256,7 @@ class WorkflowExecutionServiceTest {
workflowSessionContext.save(snapshot);
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
Map<String, Object> actualResponse = new WorkflowExecutionService().apply(workflowSessionContext, mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class),
- executionFacade, NO_OP_APPLY_SYNCHRONIZATION_HANDLER,
+ executionFacade, NO_OP_RUNTIME_HANDLER,
(workflowSnapshot, artifacts) -> List.of(new WorkflowIssue(WorkflowIssueCode.SQL_EXECUTABILITY_FAILED, "error", "review",
"Generated workflow artifact is invalid.", "Regenerate the workflow artifact before approval.", true,
Map.of("sql", artifacts.iterator().next().displaySql())).toMap()),
@@ -284,7 +295,7 @@ class WorkflowExecutionServiceTest {
WorkflowSessionContext workflowSessionContext = createWorkflowSessionContext(snapshot);
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
when(executionFacade.execute(any())).thenReturn(mock(SQLExecutionResult.class));
- Map<String, Object> actualResponse = apply(workflowSessionContext, snapshot, executionFacade, NO_OP_APPLY_SYNCHRONIZATION_HANDLER,
+ Map<String, Object> actualResponse = apply(workflowSessionContext, snapshot, executionFacade, NO_OP_RUNTIME_HANDLER,
List.of("rule_distsql"), "review-then-execute");
assertThat(actualResponse.get("status"), is("completed"));
assertThat(actualResponse.get("response_mode"), is("executed"));
@@ -303,7 +314,7 @@ class WorkflowExecutionServiceTest {
snapshot.getRuleArtifacts().add(new RuleArtifact("create", "CREATE ENCRYPT RULE orders (PROPERTIES('aes-key-value'='123456'))"));
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
when(executionFacade.execute(any())).thenReturn(mock(SQLExecutionResult.class));
- Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_APPLY_SYNCHRONIZATION_HANDLER, List.of("rule_distsql"), "review-then-execute");
+ Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_RUNTIME_HANDLER, List.of("rule_distsql"), "review-then-execute");
assertThat(actualResponse.get("status"), is("completed"));
assertThat(((Map<?, ?>) ((List<?>) actualResponse.get("step_results")).getFirst()).get("sql"),
is("CREATE ENCRYPT RULE orders (PROPERTIES('aes-key-value'='******'))"));
@@ -315,7 +326,7 @@ class WorkflowExecutionServiceTest {
void assertApplyRequiresManualExecutionForSecretReference() {
WorkflowContextSnapshot snapshot = createSecretReferenceSnapshot();
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
- Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_APPLY_SYNCHRONIZATION_HANDLER, List.of("rule_distsql"), "review-then-execute");
+ Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_RUNTIME_HANDLER, List.of("rule_distsql"), "review-then-execute");
assertThat(actualResponse.get("status"), is("failed"));
assertThat(actualResponse.get("response_mode"), is("recovery"));
assertThat(actualResponse.get("category"), is(MCPDiagnosticCategory.SECRET_REFERENCE_MANUAL_EXECUTION_REQUIRED));
@@ -337,10 +348,10 @@ class WorkflowExecutionServiceTest {
WorkflowSessionContext workflowSessionContext = createWorkflowSessionContext(snapshot);
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
when(executionFacade.execute(any())).thenReturn(mock(SQLExecutionResult.class));
- MCPWorkflowApplySynchronizationHandler workflowApplySynchronizationHandler = mock(MCPWorkflowApplySynchronizationHandler.class);
+ MCPWorkflowRuntimeHandler workflowRuntimeHandler = mock(MCPWorkflowRuntimeHandler.class);
doThrow(new WorkflowSynchronizationException(WorkflowIssueCode.RULE_STATE_MISMATCH, "Mask rule is missing.",
- List.of(Map.of("code", WorkflowIssueCode.RULE_STATE_MISMATCH)))).when(workflowApplySynchronizationHandler).synchronize(any(), any(), any(), any(), any());
- Map<String, Object> actualResponse = apply(workflowSessionContext, snapshot, executionFacade, workflowApplySynchronizationHandler,
+ List.of(Map.of("code", WorkflowIssueCode.RULE_STATE_MISMATCH)))).when(workflowRuntimeHandler).synchronize(any(), any(), any(), any(), any());
+ Map<String, Object> actualResponse = apply(workflowSessionContext, snapshot, executionFacade, workflowRuntimeHandler,
List.of("rule_distsql"), "review-then-execute");
Map<?, ?> actualIssue = (Map<?, ?>) ((List<?>) actualResponse.get("issues")).getFirst();
assertThat(actualResponse.get("status"), is("failed"));
@@ -357,7 +368,7 @@ class WorkflowExecutionServiceTest {
snapshot.getRuleArtifacts().add(new RuleArtifact("create", "CREATE ENCRYPT RULE orders (PROPERTIES('aes-key-value'='123456'))"));
MCPFeatureExecutionFacade executionFacade = mock(MCPFeatureExecutionFacade.class);
when(executionFacade.execute(any())).thenThrow(new IllegalStateException("Failed to execute CREATE ENCRYPT RULE orders (PROPERTIES('aes-key-value'='123456'))"));
- Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_APPLY_SYNCHRONIZATION_HANDLER, List.of("rule_distsql"), "review-then-execute");
+ Map<String, Object> actualResponse = apply(snapshot, executionFacade, NO_OP_RUNTIME_HANDLER, List.of("rule_distsql"), "review-then-execute");
Map<?, ?> actualIssue = (Map<?, ?>) ((List<?>) actualResponse.get("issues")).getFirst();
assertThat(actualResponse.get("status"), is("failed"));
assertThat(actualIssue.get("code"), is(WorkflowIssueCode.RULE_EXECUTION_FAILED));
@@ -366,21 +377,21 @@ class WorkflowExecutionServiceTest {
}
private Map<String, Object> apply(final WorkflowContextSnapshot snapshot, final List<String> approvedSteps, final String executionMode) {
- return apply(createWorkflowSessionContext(snapshot), snapshot, mock(MCPFeatureExecutionFacade.class), NO_OP_APPLY_SYNCHRONIZATION_HANDLER, approvedSteps, executionMode);
+ return apply(createWorkflowSessionContext(snapshot), snapshot, mock(MCPFeatureExecutionFacade.class), NO_OP_RUNTIME_HANDLER, approvedSteps, executionMode);
}
private Map<String, Object> apply(final WorkflowContextSnapshot snapshot, final MCPFeatureExecutionFacade executionFacade,
- final MCPWorkflowApplySynchronizationHandler workflowApplySynchronizationHandler, final List<String> approvedSteps,
+ final MCPWorkflowRuntimeHandler workflowRuntimeHandler, final List<String> approvedSteps,
final String executionMode) {
- return apply(createWorkflowSessionContext(snapshot), snapshot, executionFacade, workflowApplySynchronizationHandler, approvedSteps, executionMode);
+ return apply(createWorkflowSessionContext(snapshot), snapshot, executionFacade, workflowRuntimeHandler, approvedSteps, executionMode);
}
private Map<String, Object> apply(final WorkflowSessionContext workflowSessionContext, final WorkflowContextSnapshot snapshot,
final MCPFeatureExecutionFacade executionFacade,
- final MCPWorkflowApplySynchronizationHandler workflowApplySynchronizationHandler, final List<String> approvedSteps,
+ final MCPWorkflowRuntimeHandler workflowRuntimeHandler, final List<String> approvedSteps,
final String executionMode) {
return new WorkflowExecutionService().apply(workflowSessionContext, mock(MCPMetadataQueryFacade.class), mock(MCPFeatureQueryFacade.class),
- executionFacade, workflowApplySynchronizationHandler, MCPWorkflowApplyArtifactValidator.NO_OP, "session-1", snapshot, approvedSteps, executionMode);
+ executionFacade, workflowRuntimeHandler, MCPWorkflowApplyArtifactValidator.NO_OP, "session-1", snapshot, approvedSteps, executionMode);
}
private WorkflowSessionContext createWorkflowSessionContext(final WorkflowContextSnapshot snapshot) {
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowRuntimeDefinitionRegistryTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowRuntimeDefinitionRegistryTest.java
index 016ef98792c..f550657a84f 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowRuntimeDefinitionRegistryTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowRuntimeDefinitionRegistryTest.java
@@ -24,16 +24,14 @@ import org.apache.shardingsphere.mcp.core.protocol.exception.MCPWorkflowStateExc
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowKind;
import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplyArtifactValidator;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowApplySynchronizationHandler;
import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowDefinitionProvider;
-import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowValidationHandler;
+import org.apache.shardingsphere.mcp.support.workflow.spi.MCPWorkflowRuntimeHandler;
import org.apache.shardingsphere.mcp.support.workflow.spi.WorkflowRuntimeDefinition;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -95,7 +93,7 @@ class WorkflowRuntimeDefinitionRegistryTest {
@Test
void assertCreateRegistryWithNullWorkflowKind() {
WorkflowRuntimeDefinition definition = new WorkflowRuntimeDefinition(
- null, mock(MCPWorkflowValidationHandler.class), mock(MCPWorkflowApplySynchronizationHandler.class), MCPWorkflowApplyArtifactValidator.NO_OP);
+ null, mock(MCPWorkflowRuntimeHandler.class), MCPWorkflowApplyArtifactValidator.NO_OP);
NullPointerException actual = assertThrows(NullPointerException.class, () -> new WorkflowRuntimeDefinitionRegistry(List.of(definition)));
assertThat(actual.getMessage(), is(String.format("Workflow kind is required for `%s`.", definition.getClass().getName())));
}
@@ -110,25 +108,16 @@ class WorkflowRuntimeDefinitionRegistryTest {
}
@Test
- void assertCreateRegistryWithNullValidationHandler() {
+ void assertCreateRegistryWithNullRuntimeHandler() {
IllegalArgumentException actual = assertThrows(IllegalArgumentException.class, () -> new WorkflowRuntimeDefinitionRegistry(
- List.of(new WorkflowRuntimeDefinition(
- WorkflowKind.valueOf("encrypt.rule"), null, mock(MCPWorkflowApplySynchronizationHandler.class), MCPWorkflowApplyArtifactValidator.NO_OP))));
- assertThat(actual.getMessage(), is("Workflow validation handler is required for `encrypt.rule`."));
- }
-
- @Test
- void assertCreateRegistryWithNullApplySynchronizationHandler() {
- IllegalArgumentException actual = assertThrows(IllegalArgumentException.class, () -> new WorkflowRuntimeDefinitionRegistry(
- List.of(new WorkflowRuntimeDefinition(
- WorkflowKind.valueOf("encrypt.rule"), mock(MCPWorkflowValidationHandler.class), null, MCPWorkflowApplyArtifactValidator.NO_OP))));
- assertThat(actual.getMessage(), is("Workflow apply synchronization handler is required for `encrypt.rule`."));
+ List.of(new WorkflowRuntimeDefinition(WorkflowKind.valueOf("encrypt.rule"), null, MCPWorkflowApplyArtifactValidator.NO_OP))));
+ assertThat(actual.getMessage(), is("Workflow runtime handler is required for `encrypt.rule`."));
}
@Test
void assertCreateRegistryWithNullApplyArtifactValidator() {
IllegalArgumentException actual = assertThrows(IllegalArgumentException.class, () -> new WorkflowRuntimeDefinitionRegistry(
- List.of(new WorkflowRuntimeDefinition(WorkflowKind.valueOf("encrypt.rule"), mock(MCPWorkflowValidationHandler.class), mock(MCPWorkflowApplySynchronizationHandler.class), null))));
+ List.of(new WorkflowRuntimeDefinition(WorkflowKind.valueOf("encrypt.rule"), mock(MCPWorkflowRuntimeHandler.class), null))));
assertThat(actual.getMessage(), is("Workflow apply artifact validator is required for `encrypt.rule`."));
}
@@ -191,8 +180,6 @@ class WorkflowRuntimeDefinitionRegistryTest {
}
private static WorkflowRuntimeDefinition createDefinition(final String workflowKind) {
- return new WorkflowRuntimeDefinition(WorkflowKind.valueOf(workflowKind), (workflowSessionContext, metadataQueryFacade, queryFacade, executionFacade, sessionId, snapshot) -> Map.of(),
- (snapshot, metadataQueryFacade, queryFacade, executionFacade, sessionId) -> {
- }, MCPWorkflowApplyArtifactValidator.NO_OP);
+ return new WorkflowRuntimeDefinition(WorkflowKind.valueOf(workflowKind), mock(MCPWorkflowRuntimeHandler.class));
}
}
diff --git a/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastFeatureDefinitionTest.java b/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastFeatureDefinitionTest.java
index c85d325d72d..7346df001bb 100644
--- a/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastFeatureDefinitionTest.java
+++ b/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastFeatureDefinitionTest.java
@@ -28,16 +28,6 @@ import static org.hamcrest.Matchers.is;
class BroadcastFeatureDefinitionTest {
- @Test
- void assertConstants() {
- assertThat(BroadcastFeatureDefinition.WORKFLOW_KIND.getValue(), is("broadcast.rule"));
- assertThat(BroadcastFeatureDefinition.PLAN_TOOL_NAME, is("database_gateway_plan_broadcast_rule"));
- assertThat(BroadcastFeatureDefinition.PLAN_PROMPT_NAME, is("plan_broadcast_rule"));
- assertThat(BroadcastFeatureDefinition.TABLES_FIELD, is("tables"));
- assertThat(BroadcastFeatureDefinition.RULES_RESOURCE_URI, is("shardingsphere://features/broadcast/databases/{database}/rules"));
- assertThat(BroadcastFeatureDefinition.RULE_COUNT_RESOURCE_URI, is("shardingsphere://features/broadcast/databases/{database}/rule-count"));
- }
-
@Test
void assertPromptCompletionArguments() {
MCPCompletionTargetDescriptor actual = MCPDescriptorCatalogLoader.load().getShardingSphereDescriptors().getCompletionTargetDescriptors().stream()
diff --git a/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastMCPHandlerProviderTest.java b/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastMCPHandlerProviderTest.java
index 2d8baf76a11..6eb0167127c 100644
--- a/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastMCPHandlerProviderTest.java
+++ b/mcp/features/broadcast/src/test/java/org/apache/shardingsphere/mcp/feature/broadcast/BroadcastMCPHandlerProviderTest.java
@@ -61,8 +61,7 @@ class BroadcastMCPHandlerProviderTest {
void assertGetWorkflowDefinitions() {
WorkflowRuntimeDefinition actual = new BroadcastMCPHandlerProvider().getWorkflowDefinitions().iterator().next();
assertThat(actual.getWorkflowKind(), is(BroadcastFeatureDefinition.WORKFLOW_KIND));
- assertThat(actual.getApplySynchronizationHandler(), isA(BroadcastWorkflowValidationService.class));
- assertThat(actual.getValidationHandler(), isA(BroadcastWorkflowValidationService.class));
+ assertThat(actual.getRuntimeHandler(), isA(BroadcastWorkflowValidationService.class));
}
@Test
diff --git a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProvider.java b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProvider.java
index 99822737251..f1c206e0efb 100644
--- a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProvider.java
+++ b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProvider.java
@@ -57,6 +57,6 @@ public final class EncryptMCPHandlerProvider implements MCPWorkflowDefinitionPro
public Collection<WorkflowRuntimeDefinition> getWorkflowDefinitions() {
EncryptWorkflowValidationService validationService = new EncryptWorkflowValidationService();
return List.of(new WorkflowRuntimeDefinition(EncryptFeatureDefinition.WORKFLOW_KIND,
- validationService, validationService, new EncryptWorkflowApplyArtifactValidator()));
+ validationService, new EncryptWorkflowApplyArtifactValidator()));
}
}
diff --git a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/handler/EncryptWorkflowToolResponseBuilder.java b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/handler/EncryptWorkflowToolResponseBuilder.java
deleted file mode 100644
index 5cf03d7c213..00000000000
--- a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/handler/EncryptWorkflowToolResponseBuilder.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.feature.encrypt.tool.handler;
-
-import lombok.AccessLevel;
-import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.mcp.feature.encrypt.EncryptFeatureDefinition;
-import org.apache.shardingsphere.mcp.feature.encrypt.tool.model.EncryptWorkflowRequest;
-import org.apache.shardingsphere.mcp.feature.encrypt.tool.service.EncryptAlgorithmPropertyTemplateService;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
-import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
-import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowPlanPayloadBuilder;
-
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
-final class EncryptWorkflowToolResponseBuilder {
-
- private final EncryptAlgorithmPropertyTemplateService propertyTemplateService;
-
- Map<String, Object> buildPlanResponse(final WorkflowContextSnapshot snapshot) {
- WorkflowPropertySource propertySource = snapshot.getRequest();
- Map<String, Object> result = WorkflowPlanPayloadBuilder.buildWithArtifacts(snapshot, propertySource);
- result.put("masked_property_preview", createMaskedPropertyPreview(snapshot, propertySource));
- return result;
- }
-
- private Map<String, Object> createMaskedPropertyPreview(final WorkflowContextSnapshot snapshot, final WorkflowPropertySource propertySource) {
- List<AlgorithmPropertyRequirement> requirements = createPropertyRequirements(snapshot);
- Map<String, Object> result = new LinkedHashMap<>(4, 1F);
- putMaskedProperties(result, requirements, propertySource, EncryptFeatureDefinition.ALGORITHM_ROLE_PRIMARY);
- putMaskedProperties(result, requirements, propertySource, EncryptFeatureDefinition.ALGORITHM_ROLE_ASSISTED_QUERY);
- putMaskedProperties(result, requirements, propertySource, EncryptFeatureDefinition.ALGORITHM_ROLE_LIKE_QUERY);
- return result;
- }
-
- private void putMaskedProperties(final Map<String, Object> target, final List<AlgorithmPropertyRequirement> requirements,
- final WorkflowPropertySource propertySource, final String role) {
- target.put(role, propertyTemplateService.maskProperties(filterRequirements(requirements, role), propertySource.getAlgorithmProperties(role)));
- }
-
- private List<AlgorithmPropertyRequirement> createPropertyRequirements(final WorkflowContextSnapshot snapshot) {
- return snapshot.getPropertyRequirements().isEmpty() && snapshot.getRequest() instanceof final EncryptWorkflowRequest request
- ? propertyTemplateService.findRequirements(request.getAlgorithmType(), request.getOptions().getAssistedQueryAlgorithmType(), request.getOptions().getLikeQueryAlgorithmType())
- : snapshot.getPropertyRequirements();
- }
-
- private List<AlgorithmPropertyRequirement> filterRequirements(final List<AlgorithmPropertyRequirement> requirements, final String role) {
- return requirements.stream().filter(each -> role.equals(each.getAlgorithmRole())).toList();
- }
-}
diff --git a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/handler/PlanEncryptRuleToolHandler.java b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/handler/PlanEncryptRuleToolHandler.java
index a4bbad5a120..c73ae4a0339 100644
--- a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/handler/PlanEncryptRuleToolHandler.java
+++ b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/handler/PlanEncryptRuleToolHandler.java
@@ -25,11 +25,16 @@ import org.apache.shardingsphere.mcp.feature.encrypt.tool.service.EncryptWorkflo
import org.apache.shardingsphere.mcp.api.capability.tool.MCPToolHandler;
import org.apache.shardingsphere.mcp.support.protocol.payload.MCPMapPayload;
import org.apache.shardingsphere.mcp.support.MCPFeatureRequestContext;
+import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowPlanningArguments;
+import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowPlanPayloadBuilder;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowRequestBinder;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -57,7 +62,37 @@ public final class PlanEncryptRuleToolHandler implements MCPToolHandler<MCPFeatu
this::bindFeatureArguments, this::applyStructuredIntentEvidence);
WorkflowContextSnapshot snapshot = planningService.plan(requestContext.getWorkflowSessionContext(), requestContext.getMetadataQueryFacade(),
requestContext.getQueryFacade(), request);
- return new MCPMapPayload(new EncryptWorkflowToolResponseBuilder(propertyTemplateService).buildPlanResponse(snapshot));
+ return new MCPMapPayload(buildPlanResponse(snapshot));
+ }
+
+ private Map<String, Object> buildPlanResponse(final WorkflowContextSnapshot snapshot) {
+ WorkflowRequest request = snapshot.getRequest();
+ Map<String, Object> result = WorkflowPlanPayloadBuilder.buildWithArtifacts(snapshot, request);
+ result.put("masked_property_preview", createMaskedPropertyPreview(snapshot, request));
+ return result;
+ }
+
+ private Map<String, Object> createMaskedPropertyPreview(final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
+ List<AlgorithmPropertyRequirement> requirements = createPropertyRequirements(snapshot, request);
+ Map<String, Object> result = new LinkedHashMap<>(4, 1F);
+ putMaskedProperties(result, requirements, request, EncryptFeatureDefinition.ALGORITHM_ROLE_PRIMARY);
+ putMaskedProperties(result, requirements, request, EncryptFeatureDefinition.ALGORITHM_ROLE_ASSISTED_QUERY);
+ putMaskedProperties(result, requirements, request, EncryptFeatureDefinition.ALGORITHM_ROLE_LIKE_QUERY);
+ return result;
+ }
+
+ private void putMaskedProperties(final Map<String, Object> target, final List<AlgorithmPropertyRequirement> requirements,
+ final WorkflowRequest request, final String role) {
+ target.put(role, propertyTemplateService.maskProperties(
+ requirements.stream().filter(each -> role.equals(each.getAlgorithmRole())).toList(), request.getAlgorithmProperties(role)));
+ }
+
+ private List<AlgorithmPropertyRequirement> createPropertyRequirements(final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
+ if (snapshot.getPropertyRequirements().isEmpty() && request instanceof EncryptWorkflowRequest encryptRequest) {
+ return propertyTemplateService.findRequirements(encryptRequest.getAlgorithmType(), encryptRequest.getOptions().getAssistedQueryAlgorithmType(),
+ encryptRequest.getOptions().getLikeQueryAlgorithmType());
+ }
+ return snapshot.getPropertyRequirements();
}
private void bindFeatureArguments(final EncryptWorkflowRequest request, final WorkflowPlanningArguments workflowPlanningArguments) {
diff --git a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowPlanningService.java b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowPlanningService.java
index 0d38b0957f5..6efbc84b4d5 100644
--- a/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowPlanningService.java
+++ b/mcp/features/encrypt/src/main/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowPlanningService.java
@@ -119,7 +119,7 @@ public final class EncryptWorkflowPlanningService {
}
private RuleWorkflowFeatureData getWorkflowState(final WorkflowContextSnapshot snapshot) {
- return snapshot.getFeatureData() instanceof RuleWorkflowFeatureData ? (RuleWorkflowFeatureData) snapshot.getFeatureData() : new RuleWorkflowFeatureData();
+ return null == snapshot.getFeatureData() ? new RuleWorkflowFeatureData() : snapshot.getFeatureData();
}
private boolean ensureLifecycleState(final ClarifiedIntent clarifiedIntent, final EncryptWorkflowRequest request,
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 f66ca43503f..0302c91174f 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
@@ -130,7 +130,7 @@ public final class EncryptWorkflowValidationService implements MCPWorkflowRuntim
}
private Optional<List<Map<String, Object>>> getExpectedRules(final WorkflowContextSnapshot snapshot) {
- return snapshot.getFeatureData() instanceof RuleWorkflowFeatureData ? Optional.of(((RuleWorkflowFeatureData) snapshot.getFeatureData()).getExpectedRules()) : Optional.empty();
+ return Optional.ofNullable(snapshot.getFeatureData()).map(RuleWorkflowFeatureData::getExpectedRules);
}
private ValidationSection validateExpectedRules(final WorkflowContextSnapshot snapshot, final List<Map<String, Object>> expectedRules, final List<Map<String, Object>> actualRules,
diff --git a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptFeatureDefinitionTest.java b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptFeatureDefinitionTest.java
deleted file mode 100644
index d6e365a8a4b..00000000000
--- a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptFeatureDefinitionTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.feature.encrypt;
-
-import org.junit.jupiter.api.Test;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-class EncryptFeatureDefinitionTest {
-
- @Test
- void assertConstants() {
- assertThat(EncryptFeatureDefinition.WORKFLOW_KIND.getValue(), is("encrypt.rule"));
- assertThat(EncryptFeatureDefinition.PLAN_TOOL_NAME, is("database_gateway_plan_encrypt_rule"));
- assertThat(EncryptFeatureDefinition.PLAN_PROMPT_NAME, is("plan_encrypt_rule"));
- assertThat(EncryptFeatureDefinition.ALGORITHMS_RESOURCE_URI, is("shardingsphere://features/encrypt/algorithms"));
- assertThat(EncryptFeatureDefinition.RULES_RESOURCE_URI, is("shardingsphere://features/encrypt/databases/{database}/rules"));
- assertThat(EncryptFeatureDefinition.RULE_RESOURCE_URI, is("shardingsphere://features/encrypt/databases/{database}/tables/{table}/rules"));
- }
-}
diff --git a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProviderTest.java b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProviderTest.java
index f264ee7d34e..8124f1735bc 100644
--- a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProviderTest.java
+++ b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/EncryptMCPHandlerProviderTest.java
@@ -70,8 +70,7 @@ class EncryptMCPHandlerProviderTest {
void assertGetWorkflowDefinitions() {
WorkflowRuntimeDefinition actual = new EncryptMCPHandlerProvider().getWorkflowDefinitions().iterator().next();
assertThat(actual.getWorkflowKind(), is(EncryptFeatureDefinition.WORKFLOW_KIND));
- assertThat(actual.getApplySynchronizationHandler(), isA(EncryptWorkflowValidationService.class));
+ assertThat(actual.getRuntimeHandler(), isA(EncryptWorkflowValidationService.class));
assertThat(actual.getApplyArtifactValidator(), isA(EncryptWorkflowApplyArtifactValidator.class));
- assertThat(actual.getValidationHandler(), isA(EncryptWorkflowValidationService.class));
}
}
diff --git a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/completion/EncryptAlgorithmCompletionHandlerTest.java b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/completion/EncryptAlgorithmCompletionHandlerTest.java
index 4891107613c..34c5db1691e 100644
--- a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/completion/EncryptAlgorithmCompletionHandlerTest.java
+++ b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/completion/EncryptAlgorithmCompletionHandlerTest.java
@@ -42,11 +42,6 @@ import static org.mockito.Mockito.when;
class EncryptAlgorithmCompletionHandlerTest {
- @Test
- void assertGetContextType() {
- assertThat(new EncryptAlgorithmCompletionHandler().getContextType(), is(MCPFeatureRequestContext.class));
- }
-
@ParameterizedTest(name = "{0}")
@MethodSource("supportedReferences")
void assertSupports(final String name, final String referenceType, final String reference, final String argumentName) {
diff --git a/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/handler/MaskWorkflowToolResponseBuilder.java b/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/handler/MaskWorkflowToolResponseBuilder.java
deleted file mode 100644
index 180208035de..00000000000
--- a/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/handler/MaskWorkflowToolResponseBuilder.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.feature.mask.tool.handler;
-
-import lombok.AccessLevel;
-import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.mcp.feature.mask.tool.service.MaskAlgorithmPropertyTemplateService;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
-import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
-import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowPlanPayloadBuilder;
-
-import java.util.List;
-import java.util.Map;
-
-@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
-final class MaskWorkflowToolResponseBuilder {
-
- private final MaskAlgorithmPropertyTemplateService propertyTemplateService;
-
- Map<String, Object> buildPlanResponse(final WorkflowContextSnapshot snapshot) {
- WorkflowPropertySource propertySource = snapshot.getRequest();
- Map<String, Object> result = WorkflowPlanPayloadBuilder.buildWithArtifacts(snapshot, propertySource);
- result.put("masked_property_preview", createMaskedPropertyPreview(snapshot, propertySource));
- return result;
- }
-
- private Map<String, Object> createMaskedPropertyPreview(final WorkflowContextSnapshot snapshot, final WorkflowPropertySource propertySource) {
- return Map.of("primary", propertyTemplateService.maskProperties(createPropertyRequirements(snapshot), propertySource.getAlgorithmProperties("primary")));
- }
-
- private List<AlgorithmPropertyRequirement> createPropertyRequirements(final WorkflowContextSnapshot snapshot) {
- return snapshot.getPropertyRequirements().isEmpty()
- ? propertyTemplateService.findRequirements(snapshot.getRequest().getAlgorithmType())
- : snapshot.getPropertyRequirements().stream().filter(each -> "primary".equals(each.getAlgorithmRole())).toList();
- }
-
-}
diff --git a/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/handler/PlanMaskRuleToolHandler.java b/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/handler/PlanMaskRuleToolHandler.java
index ef8692fa649..1e8f429eec0 100644
--- a/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/handler/PlanMaskRuleToolHandler.java
+++ b/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/handler/PlanMaskRuleToolHandler.java
@@ -24,12 +24,15 @@ import org.apache.shardingsphere.mcp.feature.mask.tool.service.MaskWorkflowPlann
import org.apache.shardingsphere.mcp.api.capability.tool.MCPToolHandler;
import org.apache.shardingsphere.mcp.support.protocol.payload.MCPMapPayload;
import org.apache.shardingsphere.mcp.support.MCPFeatureRequestContext;
+import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowPlanningArguments;
+import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowPlanPayloadBuilder;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowRequestBinder;
+import java.util.List;
import java.util.Map;
/**
@@ -56,7 +59,21 @@ public final class PlanMaskRuleToolHandler implements MCPToolHandler<MCPFeatureR
WorkflowRequest request = WorkflowRequestBinder.bindPlanningRequest(arguments, this::bindFeatureArguments, this::applyStructuredIntentEvidence);
WorkflowContextSnapshot snapshot = planningService.plan(requestContext.getWorkflowSessionContext(), requestContext.getMetadataQueryFacade(),
requestContext.getQueryFacade(), request);
- return new MCPMapPayload(new MaskWorkflowToolResponseBuilder(propertyTemplateService).buildPlanResponse(snapshot));
+ return new MCPMapPayload(buildPlanResponse(snapshot));
+ }
+
+ private Map<String, Object> buildPlanResponse(final WorkflowContextSnapshot snapshot) {
+ WorkflowRequest request = snapshot.getRequest();
+ Map<String, Object> result = WorkflowPlanPayloadBuilder.buildWithArtifacts(snapshot, request);
+ result.put("masked_property_preview", Map.of(
+ "primary", propertyTemplateService.maskProperties(createPropertyRequirements(snapshot, request), request.getAlgorithmProperties("primary"))));
+ return result;
+ }
+
+ private List<AlgorithmPropertyRequirement> createPropertyRequirements(final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
+ return snapshot.getPropertyRequirements().isEmpty()
+ ? propertyTemplateService.findRequirements(request.getAlgorithmType())
+ : snapshot.getPropertyRequirements().stream().filter(each -> "primary".equals(each.getAlgorithmRole())).toList();
}
private void bindFeatureArguments(final WorkflowRequest request, final WorkflowPlanningArguments workflowPlanningArguments) {
diff --git a/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/service/MaskWorkflowValidationService.java b/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/service/MaskWorkflowValidationService.java
index 4100969499c..a035033edc5 100644
--- a/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/service/MaskWorkflowValidationService.java
+++ b/mcp/features/mask/src/main/java/org/apache/shardingsphere/mcp/feature/mask/tool/service/MaskWorkflowValidationService.java
@@ -148,7 +148,7 @@ public final class MaskWorkflowValidationService implements MCPWorkflowRuntimeHa
}
private Optional<RuleWorkflowFeatureData> getRuleFeatureData(final WorkflowContextSnapshot snapshot) {
- return snapshot.getFeatureData() instanceof RuleWorkflowFeatureData ? Optional.of((RuleWorkflowFeatureData) snapshot.getFeatureData()) : Optional.empty();
+ return Optional.ofNullable(snapshot.getFeatureData());
}
private ValidationSection validateExpectedRules(final WorkflowContextSnapshot snapshot, final List<Map<String, Object>> expectedRules, final List<Map<String, Object>> actualRules,
diff --git a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskFeatureDefinitionTest.java b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskFeatureDefinitionTest.java
index b5a9a57d6a2..2dce5663e3f 100644
--- a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskFeatureDefinitionTest.java
+++ b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskFeatureDefinitionTest.java
@@ -34,16 +34,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class MaskFeatureDefinitionTest {
- @Test
- void assertConstants() {
- assertThat(MaskFeatureDefinition.WORKFLOW_KIND.getValue(), is("mask.rule"));
- assertThat(MaskFeatureDefinition.PLAN_TOOL_NAME, is("database_gateway_plan_mask_rule"));
- assertThat(MaskFeatureDefinition.PLAN_PROMPT_NAME, is("plan_mask_rule"));
- assertThat(MaskFeatureDefinition.ALGORITHMS_RESOURCE_URI, is("shardingsphere://features/mask/algorithms"));
- assertThat(MaskFeatureDefinition.RULES_RESOURCE_URI, is("shardingsphere://features/mask/databases/{database}/rules"));
- assertThat(MaskFeatureDefinition.RULE_RESOURCE_URI, is("shardingsphere://features/mask/databases/{database}/tables/{table}/rules"));
- }
-
@Test
void assertPromptCompletionArguments() {
MCPCompletionTargetDescriptor actual = MCPDescriptorCatalogLoader.load().getShardingSphereDescriptors().getCompletionTargetDescriptors().stream()
diff --git a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskMCPHandlerProviderTest.java b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskMCPHandlerProviderTest.java
index f49b5eed9b4..417777ff627 100644
--- a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskMCPHandlerProviderTest.java
+++ b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/MaskMCPHandlerProviderTest.java
@@ -62,7 +62,6 @@ class MaskMCPHandlerProviderTest {
void assertGetWorkflowDefinitions() {
WorkflowRuntimeDefinition actual = new MaskMCPHandlerProvider().getWorkflowDefinitions().iterator().next();
assertThat(actual.getWorkflowKind(), is(MaskFeatureDefinition.WORKFLOW_KIND));
- assertThat(actual.getApplySynchronizationHandler(), isA(MaskWorkflowValidationService.class));
- assertThat(actual.getValidationHandler(), isA(MaskWorkflowValidationService.class));
+ assertThat(actual.getRuntimeHandler(), isA(MaskWorkflowValidationService.class));
}
}
diff --git a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/completion/MaskAlgorithmCompletionHandlerTest.java b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/completion/MaskAlgorithmCompletionHandlerTest.java
index d23da5a6deb..babdb5a6334 100644
--- a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/completion/MaskAlgorithmCompletionHandlerTest.java
+++ b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/completion/MaskAlgorithmCompletionHandlerTest.java
@@ -39,11 +39,6 @@ import static org.mockito.Mockito.when;
class MaskAlgorithmCompletionHandlerTest {
- @Test
- void assertGetContextType() {
- assertThat(new MaskAlgorithmCompletionHandler().getContextType(), is(MCPFeatureRequestContext.class));
- }
-
@Test
void assertSupports() {
assertTrue(new MaskAlgorithmCompletionHandler().supports(createRequestContext(MaskFeatureDefinition.PLAN_PROMPT_NAME)));
diff --git a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingFeatureDefinitionTest.java b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingFeatureDefinitionTest.java
index 236aef6f28b..086570f2e00 100644
--- a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingFeatureDefinitionTest.java
+++ b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingFeatureDefinitionTest.java
@@ -29,12 +29,6 @@ import static org.hamcrest.Matchers.is;
class ReadwriteSplittingFeatureDefinitionTest {
- @Test
- void assertWorkflowKinds() {
- assertThat(ReadwriteSplittingFeatureDefinition.RULE_WORKFLOW_KIND.getValue(), is("readwrite.rule"));
- assertThat(ReadwriteSplittingFeatureDefinition.STATUS_WORKFLOW_KIND.getValue(), is("readwrite.status"));
- }
-
@Test
void assertPromptCompletionArguments() {
MCPDescriptorCatalog catalog = MCPDescriptorCatalogLoader.load();
diff --git a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingMCPHandlerProviderTest.java b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingMCPHandlerProviderTest.java
index 2dee1373a21..40cb0769d14 100644
--- a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingMCPHandlerProviderTest.java
+++ b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/ReadwriteSplittingMCPHandlerProviderTest.java
@@ -69,9 +69,9 @@ class ReadwriteSplittingMCPHandlerProviderTest {
void assertGetWorkflowDefinitions() {
List<WorkflowRuntimeDefinition> actual = new ReadwriteSplittingMCPHandlerProvider().getWorkflowDefinitions().stream().toList();
assertThat(actual.get(0).getWorkflowKind(), is(ReadwriteSplittingFeatureDefinition.RULE_WORKFLOW_KIND));
- assertThat(actual.get(0).getValidationHandler(), isA(ReadwriteSplittingRuleWorkflowValidationService.class));
+ assertThat(actual.get(0).getRuntimeHandler(), isA(ReadwriteSplittingRuleWorkflowValidationService.class));
assertThat(actual.get(1).getWorkflowKind(), is(ReadwriteSplittingFeatureDefinition.STATUS_WORKFLOW_KIND));
- assertThat(actual.get(1).getValidationHandler(), isA(ReadwriteSplittingStatusWorkflowValidationService.class));
+ assertThat(actual.get(1).getRuntimeHandler(), isA(ReadwriteSplittingStatusWorkflowValidationService.class));
}
@Test
diff --git a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/completion/ReadwriteSplittingLoadBalanceAlgorithmCompletionHandlerTest.java b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/completion/ReadwriteSplittingLoadBalanceAlgorithmCompletionHandlerTest.java
index a501139159d..6886b0e39c2 100644
--- a/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/completion/ReadwriteSplittingLoadBalanceAlgorithmCompletionHandlerTest.java
+++ b/mcp/features/readwrite-splitting/src/test/java/org/apache/shardingsphere/mcp/feature/readwritesplitting/completion/ReadwriteSplittingLoadBalanceAlgorithmCompletionHandlerTest.java
@@ -39,11 +39,6 @@ import static org.mockito.Mockito.when;
class ReadwriteSplittingLoadBalanceAlgorithmCompletionHandlerTest {
- @Test
- void assertGetContextType() {
- assertThat(new ReadwriteSplittingLoadBalanceAlgorithmCompletionHandler().getContextType(), is(MCPFeatureRequestContext.class));
- }
-
@Test
void assertSupports() {
assertTrue(new ReadwriteSplittingLoadBalanceAlgorithmCompletionHandler().supports(createRequestContext(ReadwriteSplittingFeatureDefinition.PLAN_RULE_PROMPT_NAME)));
diff --git a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/ShadowFeatureDefinitionTest.java b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/ShadowFeatureDefinitionTest.java
deleted file mode 100644
index b03a74c9122..00000000000
--- a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/ShadowFeatureDefinitionTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.feature.shadow;
-
-import org.junit.jupiter.api.Test;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-class ShadowFeatureDefinitionTest {
-
- @Test
- void assertWorkflowKinds() {
- assertThat(ShadowFeatureDefinition.RULE_WORKFLOW_KIND.getValue(), is("shadow.rule"));
- assertThat(ShadowFeatureDefinition.DEFAULT_ALGORITHM_WORKFLOW_KIND.getValue(), is("shadow.default"));
- assertThat(ShadowFeatureDefinition.ALGORITHM_CLEANUP_WORKFLOW_KIND.getValue(), is("shadow.cleanup"));
- }
-}
diff --git a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/completion/ShadowAlgorithmCompletionHandlerTest.java b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/completion/ShadowAlgorithmCompletionHandlerTest.java
index 344aea66cf9..447a7b411ec 100644
--- a/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/completion/ShadowAlgorithmCompletionHandlerTest.java
+++ b/mcp/features/shadow/src/test/java/org/apache/shardingsphere/mcp/feature/shadow/completion/ShadowAlgorithmCompletionHandlerTest.java
@@ -39,11 +39,6 @@ import static org.mockito.Mockito.when;
class ShadowAlgorithmCompletionHandlerTest {
- @Test
- void assertGetContextType() {
- assertThat(new ShadowAlgorithmCompletionHandler().getContextType(), is(MCPFeatureRequestContext.class));
- }
-
@Test
void assertSupports() {
assertTrue(new ShadowAlgorithmCompletionHandler().supports(createRequestContext(ShadowFeatureDefinition.PLAN_RULE_PROMPT_NAME)));
diff --git a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingMCPHandlerProvider.java b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingMCPHandlerProvider.java
index ff045688a68..f9f1c413680 100644
--- a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingMCPHandlerProvider.java
+++ b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingMCPHandlerProvider.java
@@ -92,11 +92,11 @@ public final class ShardingMCPHandlerProvider implements MCPWorkflowDefinitionPr
ShardingWorkflowValidationService validationService = new ShardingWorkflowValidationService();
ShardingWorkflowApplyArtifactValidator applyArtifactValidator = new ShardingWorkflowApplyArtifactValidator();
return List.of(
- new WorkflowRuntimeDefinition(ShardingFeatureDefinition.TABLE_RULE_WORKFLOW_KIND, validationService, validationService, applyArtifactValidator),
- new WorkflowRuntimeDefinition(ShardingFeatureDefinition.TABLE_REFERENCE_WORKFLOW_KIND, validationService, validationService, applyArtifactValidator),
- new WorkflowRuntimeDefinition(ShardingFeatureDefinition.DEFAULT_STRATEGY_WORKFLOW_KIND, validationService, validationService, applyArtifactValidator),
- new WorkflowRuntimeDefinition(ShardingFeatureDefinition.KEY_GENERATOR_WORKFLOW_KIND, validationService, validationService, applyArtifactValidator),
- new WorkflowRuntimeDefinition(ShardingFeatureDefinition.KEY_GENERATE_STRATEGY_WORKFLOW_KIND, validationService, validationService, applyArtifactValidator),
- new WorkflowRuntimeDefinition(ShardingFeatureDefinition.COMPONENT_CLEANUP_WORKFLOW_KIND, validationService, validationService, applyArtifactValidator));
+ new WorkflowRuntimeDefinition(ShardingFeatureDefinition.TABLE_RULE_WORKFLOW_KIND, validationService, applyArtifactValidator),
+ new WorkflowRuntimeDefinition(ShardingFeatureDefinition.TABLE_REFERENCE_WORKFLOW_KIND, validationService, applyArtifactValidator),
+ new WorkflowRuntimeDefinition(ShardingFeatureDefinition.DEFAULT_STRATEGY_WORKFLOW_KIND, validationService, applyArtifactValidator),
+ new WorkflowRuntimeDefinition(ShardingFeatureDefinition.KEY_GENERATOR_WORKFLOW_KIND, validationService, applyArtifactValidator),
+ new WorkflowRuntimeDefinition(ShardingFeatureDefinition.KEY_GENERATE_STRATEGY_WORKFLOW_KIND, validationService, applyArtifactValidator),
+ new WorkflowRuntimeDefinition(ShardingFeatureDefinition.COMPONENT_CLEANUP_WORKFLOW_KIND, validationService, applyArtifactValidator));
}
}
diff --git a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/handler/ShardingPlanningRequestBinder.java b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/handler/ShardingPlanningRequestBinder.java
index 78462be1a9a..9d0e9388d7b 100644
--- a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/handler/ShardingPlanningRequestBinder.java
+++ b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/handler/ShardingPlanningRequestBinder.java
@@ -26,6 +26,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Stream;
@@ -94,8 +95,10 @@ public final class ShardingPlanningRequestBinder {
return bind(arguments, this::bindRuleComponentCleanupArguments, this::applyRuleComponentCleanupEvidence);
}
- private ShardingWorkflowRequest bind(final Map<String, Object> arguments, final FeatureArgumentBinder argumentBinder, final EvidenceBinder evidenceBinder) {
- return WorkflowRequestBinder.bindPlanningRequest(ShardingWorkflowRequest::new, arguments, argumentBinder::bind, evidenceBinder::bind);
+ private ShardingWorkflowRequest bind(final Map<String, Object> arguments,
+ final BiConsumer<ShardingWorkflowRequest, WorkflowPlanningArguments> argumentBinder,
+ final BiConsumer<ShardingWorkflowRequest, Map<String, Object>> evidenceBinder) {
+ return WorkflowRequestBinder.bindPlanningRequest(ShardingWorkflowRequest::new, arguments, argumentBinder, evidenceBinder);
}
private void bindTableRuleArguments(final ShardingWorkflowRequest request, final WorkflowPlanningArguments workflowPlanningArguments) {
@@ -232,15 +235,4 @@ public final class ShardingPlanningRequestBinder {
return Stream.of(String.valueOf(value).split(",")).map(String::trim).filter(each -> !each.isEmpty()).toList();
}
- @FunctionalInterface
- private interface FeatureArgumentBinder {
-
- void bind(ShardingWorkflowRequest request, WorkflowPlanningArguments workflowPlanningArguments);
- }
-
- @FunctionalInterface
- private interface EvidenceBinder {
-
- void bind(ShardingWorkflowRequest request, Map<String, Object> values);
- }
}
diff --git a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowLifecycleSpec.java b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowLifecycleSpec.java
deleted file mode 100644
index 5c5f740dd47..00000000000
--- a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowLifecycleSpec.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.feature.sharding.tool.service;
-
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.mcp.feature.sharding.tool.model.ShardingWorkflowRequest;
-import org.apache.shardingsphere.mcp.support.workflow.model.RuleArtifact;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowKind;
-
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
-@Getter(AccessLevel.PACKAGE)
-final class ShardingWorkflowLifecycleSpec {
-
- private final WorkflowKind workflowKind;
-
- private final String defaultOperationType;
-
- private final String summary;
-
- private final Function<ShardingWorkflowRequest, Boolean> existsSupplier;
-
- private final BiFunction<ShardingWorkflowRequest, WorkflowContextSnapshot, Boolean> requiredInputSupplier;
-
- private final BiFunction<ShardingWorkflowRequest, WorkflowContextSnapshot, Boolean> algorithmPlanSupplier;
-
- private final Function<ShardingWorkflowRequest, RuleArtifact> artifactSupplier;
-
-}
diff --git a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowPlanningService.java b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowPlanningService.java
index f5ebb4f0a5f..c9b1d4485ae 100644
--- a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowPlanningService.java
+++ b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowPlanningService.java
@@ -25,6 +25,7 @@ import org.apache.shardingsphere.mcp.support.workflow.WorkflowSessionContext;
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmCandidate;
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
import org.apache.shardingsphere.mcp.support.workflow.model.ClarifiedIntent;
+import org.apache.shardingsphere.mcp.support.workflow.model.RuleArtifact;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowIssue;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowIssueCode;
@@ -37,6 +38,8 @@ import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowRuleValueU
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
/**
* Sharding workflow planning service.
@@ -204,7 +207,7 @@ public final class ShardingWorkflowPlanningService {
private WorkflowContextSnapshot planLifecycleWorkflow(final WorkflowSessionContext workflowSessionContext, final ShardingWorkflowRequest request,
final ShardingWorkflowLifecycleSpec spec) {
- WorkflowContextSnapshot result = prepareSnapshot(workflowSessionContext, request, spec.getWorkflowKind(), spec.getDefaultOperationType(), spec.getSummary());
+ WorkflowContextSnapshot result = prepareSnapshot(workflowSessionContext, request, spec.workflowKind(), spec.defaultOperationType(), spec.summary());
ShardingWorkflowRequest mergedRequest = (ShardingWorkflowRequest) result.getRequest();
if (!planningSupport.ensureSupportedOperationType(result.getClarifiedIntent(), SUPPORTED_LIFECYCLE_OPERATION_TYPES, result)) {
return workflowSessionContext.persist(result, WorkflowLifecycle.STEP_FAILED, WorkflowLifecycle.STATUS_FAILED);
@@ -215,20 +218,20 @@ public final class ShardingWorkflowPlanningService {
if (!inputValidator.ensureIdentifiers(mergedRequest, result)) {
return workflowSessionContext.persist(result, WorkflowLifecycle.STEP_FAILED, result.getStatus());
}
- if (!inputValidator.ensureCompatibleInputs(spec.getWorkflowKind(), mergedRequest, result)) {
+ if (!inputValidator.ensureCompatibleInputs(spec.workflowKind(), mergedRequest, result)) {
return workflowSessionContext.persist(result, WorkflowLifecycle.STEP_FAILED, result.getStatus());
}
- if (!spec.getRequiredInputSupplier().apply(mergedRequest, result)) {
+ if (!spec.requiredInputSupplier().apply(mergedRequest, result)) {
addRequiredInputClarification(mergedRequest, result);
return workflowSessionContext.persist(result, WorkflowLifecycle.STEP_CLARIFYING, result.getStatus());
}
- if (!planningSupport.ensureLifecycleState(spec.getSummary(), result.getClarifiedIntent(), spec.getExistsSupplier().apply(mergedRequest), result)) {
+ if (!planningSupport.ensureLifecycleState(spec.summary(), result.getClarifiedIntent(), spec.existsSupplier().apply(mergedRequest), result)) {
return workflowSessionContext.persist(result, WorkflowLifecycle.STEP_FAILED, WorkflowLifecycle.STATUS_FAILED);
}
- if (!spec.getAlgorithmPlanSupplier().apply(mergedRequest, result)) {
+ if (!spec.algorithmPlanSupplier().apply(mergedRequest, result)) {
return workflowSessionContext.persist(result, WorkflowLifecycle.STEP_CLARIFYING, WorkflowLifecycle.STATUS_CLARIFYING);
}
- result.getRuleArtifacts().add(spec.getArtifactSupplier().apply(mergedRequest));
+ result.getRuleArtifacts().add(spec.artifactSupplier().apply(mergedRequest));
return workflowSessionContext.persist(result, WorkflowLifecycle.STEP_REVIEW, WorkflowLifecycle.STATUS_PLANNED);
}
@@ -346,4 +349,11 @@ public final class ShardingWorkflowPlanningService {
return rows.stream().anyMatch(each -> queryFacade.isSameIdentifier(databaseName, IdentifierScope.TABLE, expected, WorkflowRuleValueUtils.getRuleValue(each, fieldName)));
}
+ private record ShardingWorkflowLifecycleSpec(WorkflowKind workflowKind, String defaultOperationType, String summary,
+ Function<ShardingWorkflowRequest, Boolean> existsSupplier,
+ BiFunction<ShardingWorkflowRequest, WorkflowContextSnapshot, Boolean> requiredInputSupplier,
+ BiFunction<ShardingWorkflowRequest, WorkflowContextSnapshot, Boolean> algorithmPlanSupplier,
+ Function<ShardingWorkflowRequest, RuleArtifact> artifactSupplier) {
+ }
+
}
diff --git a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingFeatureDefinitionTest.java b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingFeatureDefinitionTest.java
deleted file mode 100644
index 0d105f3c187..00000000000
--- a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingFeatureDefinitionTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.feature.sharding;
-
-import org.junit.jupiter.api.Test;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-class ShardingFeatureDefinitionTest {
-
- @Test
- void assertConstants() {
- assertThat(ShardingFeatureDefinition.TABLE_RULE_WORKFLOW_KIND.getValue(), is("sharding.table.rule"));
- assertThat(ShardingFeatureDefinition.TABLE_REFERENCE_WORKFLOW_KIND.getValue(), is("sharding.table.reference"));
- assertThat(ShardingFeatureDefinition.DEFAULT_STRATEGY_WORKFLOW_KIND.getValue(), is("sharding.default.strategy"));
- assertThat(ShardingFeatureDefinition.KEY_GENERATOR_WORKFLOW_KIND.getValue(), is("sharding.key.generator"));
- assertThat(ShardingFeatureDefinition.KEY_GENERATE_STRATEGY_WORKFLOW_KIND.getValue(), is("sharding.key.generate.strategy"));
- assertThat(ShardingFeatureDefinition.COMPONENT_CLEANUP_WORKFLOW_KIND.getValue(), is("sharding.component.cleanup"));
- assertThat(ShardingFeatureDefinition.PLAN_TABLE_RULE_TOOL_NAME, is("database_gateway_plan_sharding_table_rule"));
- assertThat(ShardingFeatureDefinition.PLAN_TABLE_RULE_PROMPT_NAME, is("plan_sharding_table_rule"));
- assertThat(ShardingFeatureDefinition.PLAN_TABLE_REFERENCE_PROMPT_NAME, is("plan_sharding_table_reference_rule"));
- assertThat(ShardingFeatureDefinition.PLAN_DEFAULT_STRATEGY_PROMPT_NAME, is("plan_sharding_default_strategy"));
- assertThat(ShardingFeatureDefinition.PLAN_KEY_GENERATOR_PROMPT_NAME, is("plan_sharding_key_generator"));
- assertThat(ShardingFeatureDefinition.PLAN_KEY_GENERATE_STRATEGY_PROMPT_NAME, is("plan_sharding_key_generate_strategy"));
- assertThat(ShardingFeatureDefinition.PLAN_COMPONENT_CLEANUP_PROMPT_NAME, is("plan_sharding_rule_component_cleanup"));
- assertThat(ShardingFeatureDefinition.ALGORITHM_PLUGINS_RESOURCE_URI, is("shardingsphere://features/sharding/algorithm-plugins"));
- assertThat(ShardingFeatureDefinition.RULE_COUNT_RESOURCE_URI, is("shardingsphere://features/sharding/databases/{database}/rule-count"));
- }
-}
diff --git a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingMCPHandlerProviderTest.java b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingMCPHandlerProviderTest.java
index 50e56bbb79f..debf2869f61 100644
--- a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingMCPHandlerProviderTest.java
+++ b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/ShardingMCPHandlerProviderTest.java
@@ -75,8 +75,7 @@ class ShardingMCPHandlerProviderTest {
assertThat(actual.stream().map(each -> each.getWorkflowKind().getValue()).toList(),
containsInAnyOrder("sharding.table.rule", "sharding.table.reference", "sharding.default.strategy",
"sharding.key.generator", "sharding.key.generate.strategy", "sharding.component.cleanup"));
- assertTrue(actual.stream().allMatch(each -> each.getValidationHandler() instanceof ShardingWorkflowValidationService));
- assertTrue(actual.stream().allMatch(each -> each.getApplySynchronizationHandler() instanceof ShardingWorkflowValidationService));
+ assertTrue(actual.stream().allMatch(each -> each.getRuntimeHandler() instanceof ShardingWorkflowValidationService));
assertTrue(actual.stream().allMatch(each -> each.getApplyArtifactValidator() instanceof ShardingWorkflowApplyArtifactValidator));
}
diff --git a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/completion/ShardingAlgorithmCompletionHandlerTest.java b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/completion/ShardingAlgorithmCompletionHandlerTest.java
index 43123c32eca..69040bc45c0 100644
--- a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/completion/ShardingAlgorithmCompletionHandlerTest.java
+++ b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/completion/ShardingAlgorithmCompletionHandlerTest.java
@@ -39,11 +39,6 @@ import static org.mockito.Mockito.when;
class ShardingAlgorithmCompletionHandlerTest {
- @Test
- void assertGetContextType() {
- assertThat(new ShardingAlgorithmCompletionHandler().getContextType(), is(MCPFeatureRequestContext.class));
- }
-
@Test
void assertSupportsShardingAlgorithm() {
assertTrue(new ShardingAlgorithmCompletionHandler().supports(createRequestContext(ShardingFeatureDefinition.ALGORITHM_PLUGINS_RESOURCE_URI, "algorithm_type")));
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilder.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilder.java
index 57d6de6e0a6..7637183d018 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilder.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilder.java
@@ -21,7 +21,7 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.shardingsphere.mcp.support.protocol.MCPModelFacingPayloadContract;
import org.apache.shardingsphere.mcp.support.protocol.MCPResponseMode;
-import org.apache.shardingsphere.mcp.support.security.MCPClientSafetyPolicy;
+import org.apache.shardingsphere.mcp.support.security.MCPRuntimeProtectionPolicy;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -117,7 +117,20 @@ final class MCPGuidancePayloadBuilder {
result.put("http_transport", "Streamable HTTP is unauthenticated by default; prefer loopback binding or put remote exposure behind a trusted gateway.");
result.put("origin_header", "Present Origin headers must be valid loopback origins for loopback HTTP bindings; missing Origin is accepted.");
result.put("stdio_stdout", "STDIO transport must keep MCP protocol frames on stdout and send logs to stderr or files.");
- result.put("client_safety_policy", MCPClientSafetyPolicy.createModelFacingPayload());
+ result.put("client_safety_policy", createClientSafetyPolicy());
+ return result;
+ }
+
+ private static Map<String, Object> createClientSafetyPolicy() {
+ Map<String, Object> result = new LinkedHashMap<>(5, 1F);
+ result.put("identity_scope", "mcp_session");
+ result.put("transport_scope",
+ "HTTP transport can bind trusted session attribution when configured; "
+ + "the MCP runtime does not provide built-in authentication or authorization. "
+ + "STDIO inherits the local process boundary.");
+ result.put("runtime_protection", MCPRuntimeProtectionPolicy.createRuntimeProtectionPayload());
+ result.put("abuse_guard", "Every tool call is counted before dispatch, including invalid calls, so runaway model loops stop at the session quota.");
+ result.put("external_model_boundary", "The MCP runtime never calls external model providers; live LLM E2E clients call configured endpoints outside the server.");
return result;
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/security/MCPClientSafetyPolicy.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/security/MCPClientSafetyPolicy.java
deleted file mode 100644
index 5f65c923ee1..00000000000
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/security/MCPClientSafetyPolicy.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.security;
-
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * MCP client-facing safety policy.
- */
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public final class MCPClientSafetyPolicy {
-
- /**
- * Create model-facing safety policy payload.
- *
- * @return model-facing safety policy payload
- */
- public static Map<String, Object> createModelFacingPayload() {
- Map<String, Object> result = new LinkedHashMap<>(5, 1F);
- result.put("identity_scope", "mcp_session");
- result.put("transport_scope",
- "HTTP transport can bind trusted session attribution when configured; "
- + "the MCP runtime does not provide built-in authentication or authorization. "
- + "STDIO inherits the local process boundary.");
- result.put("runtime_protection", MCPRuntimeProtectionPolicy.createRuntimeProtectionPayload());
- result.put("abuse_guard", "Every tool call is counted before dispatch, including invalid calls, so runaway model loops stop at the session quota.");
- result.put("external_model_boundary", "The MCP runtime never calls external model providers; live LLM E2E clients call configured endpoints outside the server.");
- return result;
- }
-}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/WorkflowPropertySource.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/WorkflowPropertySource.java
deleted file mode 100644
index c7cd1f153fa..00000000000
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/WorkflowPropertySource.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.workflow;
-
-import org.apache.shardingsphere.mcp.support.workflow.model.SecretReferenceValue;
-
-import java.util.Map;
-
-/**
- * Workflow algorithm property source.
- */
-public interface WorkflowPropertySource {
-
- /**
- * Get algorithm properties by role.
- *
- * @param algorithmRole algorithm role
- * @return algorithm properties
- */
- Map<String, String> getAlgorithmProperties(String algorithmRole);
-
- /**
- * Get secret references grouped by algorithm role.
- *
- * @return secret references
- */
- default Map<String, Map<String, SecretReferenceValue>> getSecretReferences() {
- return Map.of();
- }
-
- /**
- * Get secret references by role.
- *
- * @param algorithmRole algorithm role
- * @return secret references
- */
- default Map<String, SecretReferenceValue> getSecretReferences(final String algorithmRole) {
- return getSecretReferences().getOrDefault(algorithmRole, Map.of());
- }
-}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/RuleWorkflowFeatureData.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/RuleWorkflowFeatureData.java
index ba35295ddac..1b1c9672393 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/RuleWorkflowFeatureData.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/RuleWorkflowFeatureData.java
@@ -20,7 +20,6 @@ package org.apache.shardingsphere.mcp.support.workflow.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
-import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -30,43 +29,15 @@ import java.util.Map;
*/
@Getter
@NoArgsConstructor
-public final class RuleWorkflowFeatureData implements WorkflowFeatureData {
+public final class RuleWorkflowFeatureData {
private final List<Map<String, Object>> expectedRules = new LinkedList<>();
public RuleWorkflowFeatureData(final List<Map<String, Object>> expectedRules) {
- this.expectedRules.addAll(copyRules(expectedRules));
+ expectedRules.forEach(each -> this.expectedRules.add(WorkflowContextSnapshot.copyMap(each)));
}
- @Override
- public RuleWorkflowFeatureData copy() {
+ RuleWorkflowFeatureData copy() {
return new RuleWorkflowFeatureData(expectedRules);
}
-
- private List<Map<String, Object>> copyRules(final List<Map<String, Object>> rules) {
- List<Map<String, Object>> result = new LinkedList<>();
- for (Map<String, Object> each : rules) {
- result.add(copyMap(each));
- }
- return result;
- }
-
- private Map<String, Object> copyMap(final Map<String, Object> original) {
- Map<String, Object> result = new LinkedHashMap<>(original.size(), 1F);
- original.forEach((key, value) -> result.put(key, copyValue(value)));
- return result;
- }
-
- private Object copyValue(final Object original) {
- if (original instanceof Map) {
- Map<?, ?> originalMap = (Map<?, ?>) original;
- Map<String, Object> result = new LinkedHashMap<>(originalMap.size(), 1F);
- originalMap.forEach((key, value) -> result.put(String.valueOf(key), copyValue(value)));
- return result;
- }
- if (original instanceof List) {
- return ((List<?>) original).stream().map(this::copyValue).toList();
- }
- return original;
- }
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowContextSnapshot.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowContextSnapshot.java
index a0fd5a39b75..c96578f324d 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowContextSnapshot.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowContextSnapshot.java
@@ -48,7 +48,7 @@ public final class WorkflowContextSnapshot {
private ClarifiedIntent clarifiedIntent;
- private WorkflowFeatureData featureData;
+ private RuleWorkflowFeatureData featureData;
private InteractionPlan interactionPlan;
@@ -156,7 +156,7 @@ public final class WorkflowContextSnapshot {
return new ValidationSection(original.getStatus(), copyValue(original.getEvidence()), copyValue(original.getDetails()));
}
- private static Map<String, Object> copyMap(final Map<?, ?> original) {
+ static Map<String, Object> copyMap(final Map<?, ?> original) {
Map<String, Object> result = new LinkedHashMap<>(original.size(), 1F);
original.forEach((key, value) -> result.put(String.valueOf(key), copyValue(value)));
return result;
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowFeatureData.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowFeatureData.java
deleted file mode 100644
index a93d74ed791..00000000000
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowFeatureData.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.workflow.model;
-
-/**
- * Feature-scoped workflow data.
- */
-public interface WorkflowFeatureData {
-
- /**
- * Copy feature data.
- *
- * @return copied feature data
- */
- WorkflowFeatureData copy();
-}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowRequest.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowRequest.java
index a325b31e70a..77687af54cc 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowRequest.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowRequest.java
@@ -18,8 +18,6 @@
package org.apache.shardingsphere.mcp.support.workflow.model;
import lombok.Getter;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
-
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -29,7 +27,7 @@ import java.util.Map;
* Workflow request.
*/
@Getter
-public class WorkflowRequest implements WorkflowPropertySource {
+public class WorkflowRequest {
private String planId = "";
@@ -140,12 +138,21 @@ public class WorkflowRequest implements WorkflowPropertySource {
return null == source ? target : source.copyTo(target);
}
- @Override
+ /**
+ * Get algorithm properties.
+ *
+ * @param algorithmRole algorithm role
+ * @return algorithm properties
+ */
public Map<String, String> getAlgorithmProperties(final String algorithmRole) {
return "primary".equals(algorithmRole) ? primaryAlgorithmProperties : Map.of();
}
- @Override
+ /**
+ * Get secret references by algorithm role.
+ *
+ * @return secret references by algorithm role
+ */
public Map<String, Map<String, SecretReferenceValue>> getSecretReferences() {
if (primaryAlgorithmSecretReferences.isEmpty()) {
return Map.of();
@@ -155,7 +162,12 @@ public class WorkflowRequest implements WorkflowPropertySource {
return result;
}
- @Override
+ /**
+ * Get secret references.
+ *
+ * @param algorithmRole algorithm role
+ * @return secret references
+ */
public Map<String, SecretReferenceValue> getSecretReferences(final String algorithmRole) {
return "primary".equals(algorithmRole) ? primaryAlgorithmSecretReferences : Map.of();
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactBundle.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactBundle.java
index baaa9071771..b835d9f0238 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactBundle.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactBundle.java
@@ -19,10 +19,10 @@ package org.apache.shardingsphere.mcp.support.workflow.service;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
import org.apache.shardingsphere.mcp.support.workflow.model.RuleArtifact;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import java.util.Collection;
import java.util.List;
@@ -49,22 +49,22 @@ public final class WorkflowArtifactBundle {
/**
* Convert workflow artifacts into executable artifacts with masked display SQL.
*
- * @param propertySource workflow property source
+ * @param request workflow request
* @param propertyRequirements property requirements
* @return executable workflow artifacts
*/
- public List<ExecutableWorkflowArtifact> toExecutableArtifacts(final WorkflowPropertySource propertySource, final List<AlgorithmPropertyRequirement> propertyRequirements) {
- return ruleArtifacts.stream().map(each -> createExecutableArtifact(each, propertySource, propertyRequirements)).toList();
+ public List<ExecutableWorkflowArtifact> toExecutableArtifacts(final WorkflowRequest request, final List<AlgorithmPropertyRequirement> propertyRequirements) {
+ return ruleArtifacts.stream().map(each -> createExecutableArtifact(each, request, propertyRequirements)).toList();
}
- private ExecutableWorkflowArtifact createExecutableArtifact(final RuleArtifact ruleArtifact, final WorkflowPropertySource propertySource,
+ private ExecutableWorkflowArtifact createExecutableArtifact(final RuleArtifact ruleArtifact, final WorkflowRequest request,
final List<AlgorithmPropertyRequirement> propertyRequirements) {
- return new ExecutableWorkflowArtifact(ruleArtifact.getSql(), WorkflowArtifactMaskUtils.maskSensitiveSql(ruleArtifact.getSql(), propertySource, propertyRequirements));
+ return new ExecutableWorkflowArtifact(ruleArtifact.getSql(), WorkflowArtifactMaskUtils.maskSensitiveSql(ruleArtifact.getSql(), request, propertyRequirements));
}
- Map<String, Object> toPayload(final WorkflowPropertySource propertySource, final List<AlgorithmPropertyRequirement> propertyRequirements) {
+ Map<String, Object> toPayload(final WorkflowRequest request, final List<AlgorithmPropertyRequirement> propertyRequirements) {
return Map.of(WorkflowArtifactPayloadUtils.PAYLOAD_KEY_DISTSQL_ARTIFACTS, ruleArtifacts.stream()
- .map(each -> WorkflowArtifactMaskUtils.createMaskedRuleArtifactMap(each, propertySource, propertyRequirements)).toList());
+ .map(each -> WorkflowArtifactMaskUtils.createMaskedRuleArtifactMap(each, request, propertyRequirements)).toList());
}
public record ExecutableWorkflowArtifact(String sql, String displaySql) {
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactMaskUtils.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactMaskUtils.java
index 2c9ca530416..161967e0d12 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactMaskUtils.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactMaskUtils.java
@@ -19,11 +19,11 @@ package org.apache.shardingsphere.mcp.support.workflow.service;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
import org.apache.shardingsphere.mcp.support.workflow.model.RuleArtifact;
import org.apache.shardingsphere.mcp.support.workflow.model.SecretReferenceValue;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -42,16 +42,16 @@ public final class WorkflowArtifactMaskUtils {
* Create masked rule artifact map.
*
* @param ruleArtifact rule artifact
- * @param propertySource workflow property source
+ * @param request workflow request
* @param propertyRequirements property requirements
* @return masked rule artifact map
*/
- public static Map<String, Object> createMaskedRuleArtifactMap(final RuleArtifact ruleArtifact, final WorkflowPropertySource propertySource,
+ public static Map<String, Object> createMaskedRuleArtifactMap(final RuleArtifact ruleArtifact, final WorkflowRequest request,
final List<AlgorithmPropertyRequirement> propertyRequirements) {
Map<String, Object> result = new LinkedHashMap<>(4, 1F);
result.put(WorkflowFieldNames.OPERATION_TYPE, ruleArtifact.getOperationType());
- result.put("sql", maskSensitiveSql(ruleArtifact.getSql(), propertySource, propertyRequirements));
- result.put("redaction", createRedactionPayload(propertySource, propertyRequirements));
+ result.put("sql", maskSensitiveSql(ruleArtifact.getSql(), request, propertyRequirements));
+ result.put("redaction", createRedactionPayload(request, propertyRequirements));
return result;
}
@@ -59,13 +59,13 @@ public final class WorkflowArtifactMaskUtils {
* Mask sensitive values inside SQL text.
*
* @param sql SQL text
- * @param propertySource workflow property source
+ * @param request workflow request
* @param propertyRequirements property requirements
* @return masked SQL text
*/
- public static String maskSensitiveSql(final String sql, final WorkflowPropertySource propertySource, final List<AlgorithmPropertyRequirement> propertyRequirements) {
- String result = WorkflowSecretReferenceUtils.replacePlaceholdersWithManualPlaceholders(sql, propertySource);
- for (String each : collectSecretValues(propertySource, propertyRequirements)) {
+ public static String maskSensitiveSql(final String sql, final WorkflowRequest request, final List<AlgorithmPropertyRequirement> propertyRequirements) {
+ String result = WorkflowSecretReferenceUtils.replacePlaceholdersWithManualPlaceholders(sql, request);
+ for (String each : collectSecretValues(request, propertyRequirements)) {
if (each.isEmpty()) {
continue;
}
@@ -93,14 +93,14 @@ public final class WorkflowArtifactMaskUtils {
*
* @param properties property values
* @param propertyRequirements property requirements
- * @param propertySource workflow property source
+ * @param request workflow request
* @param algorithmRole algorithm role
* @return masked property values
*/
public static Map<String, String> maskPropertyMap(final Map<String, String> properties, final List<AlgorithmPropertyRequirement> propertyRequirements,
- final WorkflowPropertySource propertySource, final String algorithmRole) {
+ final WorkflowRequest request, final String algorithmRole) {
Map<String, String> result = new LinkedHashMap<>(properties.size(), 1F);
- properties.forEach((key, value) -> result.put(key, isSecretProperty(propertyRequirements, key) || isSecretReferenceProperty(propertySource, algorithmRole, key)
+ properties.forEach((key, value) -> result.put(key, isSecretProperty(propertyRequirements, key) || isSecretReferenceProperty(request, algorithmRole, key)
|| isSecretReferencePlaceholder(value) ? "******" : value));
return result;
}
@@ -108,11 +108,11 @@ public final class WorkflowArtifactMaskUtils {
/**
* Create secret reference summary payload.
*
- * @param propertySource workflow property source
+ * @param request workflow request
* @return secret reference summary payload
*/
- public static Map<String, Object> createSecretReferenceSummary(final WorkflowPropertySource propertySource) {
- List<Map<String, Object>> references = WorkflowSecretReferenceUtils.createSafeSummaries(propertySource);
+ public static Map<String, Object> createSecretReferenceSummary(final WorkflowRequest request) {
+ List<Map<String, Object>> references = WorkflowSecretReferenceUtils.createSafeSummaries(request);
Map<String, Object> result = new LinkedHashMap<>(4, 1F);
result.put("required", !references.isEmpty());
result.put("reference_count", references.size());
@@ -121,8 +121,8 @@ public final class WorkflowArtifactMaskUtils {
return result;
}
- private static Map<String, Object> createRedactionPayload(final WorkflowPropertySource propertySource, final List<AlgorithmPropertyRequirement> propertyRequirements) {
- List<String> redactedProperties = collectSecretPropertyNames(propertySource, propertyRequirements).stream().toList();
+ private static Map<String, Object> createRedactionPayload(final WorkflowRequest request, final List<AlgorithmPropertyRequirement> propertyRequirements) {
+ List<String> redactedProperties = collectSecretPropertyNames(request, propertyRequirements).stream().toList();
Map<String, Object> result = new LinkedHashMap<>(5, 1F);
result.put("applied", !redactedProperties.isEmpty());
result.put("marker", "******");
@@ -137,40 +137,40 @@ public final class WorkflowArtifactMaskUtils {
return 0 > separatorIndex ? redactedProperty : redactedProperty.substring(separatorIndex + 1);
}
- private static Set<String> collectSecretValues(final WorkflowPropertySource propertySource, final List<AlgorithmPropertyRequirement> propertyRequirements) {
+ private static Set<String> collectSecretValues(final WorkflowRequest request, final List<AlgorithmPropertyRequirement> propertyRequirements) {
Set<String> result = new LinkedHashSet<>();
for (AlgorithmPropertyRequirement each : propertyRequirements) {
if (!each.isSecret()) {
continue;
}
- String value = getPropertyValue(propertySource, each);
+ String value = getPropertyValue(request, each);
if (null != value && !value.isBlank()) {
result.add(value.trim());
}
}
- propertySource.getSecretReferences().forEach((algorithmRole, references) -> references.keySet().forEach(
+ request.getSecretReferences().forEach((algorithmRole, references) -> references.keySet().forEach(
propertyKey -> result.add(SecretReferenceValue.createPlaceholder(algorithmRole, propertyKey))));
return result;
}
- private static Set<String> collectSecretPropertyNames(final WorkflowPropertySource propertySource, final List<AlgorithmPropertyRequirement> propertyRequirements) {
+ private static Set<String> collectSecretPropertyNames(final WorkflowRequest request, final List<AlgorithmPropertyRequirement> propertyRequirements) {
Set<String> result = new LinkedHashSet<>();
for (AlgorithmPropertyRequirement each : propertyRequirements) {
if (!each.isSecret()) {
continue;
}
- String value = getPropertyValue(propertySource, each);
+ String value = getPropertyValue(request, each);
if (null != value && !value.isBlank()) {
result.add(each.getAlgorithmRole() + "." + each.getPropertyKey());
}
}
- propertySource.getSecretReferences().forEach((algorithmRole, references) -> references.keySet().forEach(
+ request.getSecretReferences().forEach((algorithmRole, references) -> references.keySet().forEach(
propertyKey -> result.add(algorithmRole + "." + propertyKey)));
return result;
}
- private static String getPropertyValue(final WorkflowPropertySource propertySource, final AlgorithmPropertyRequirement propertyRequirement) {
- return propertySource.getAlgorithmProperties(propertyRequirement.getAlgorithmRole()).get(propertyRequirement.getPropertyKey());
+ private static String getPropertyValue(final WorkflowRequest request, final AlgorithmPropertyRequirement propertyRequirement) {
+ return request.getAlgorithmProperties(propertyRequirement.getAlgorithmRole()).get(propertyRequirement.getPropertyKey());
}
private static boolean isSecretProperty(final List<AlgorithmPropertyRequirement> propertyRequirements, final String propertyKey) {
@@ -189,7 +189,7 @@ public final class WorkflowArtifactMaskUtils {
return null != value && value.startsWith("secret_reference:");
}
- private static boolean isSecretReferenceProperty(final WorkflowPropertySource propertySource, final String algorithmRole, final String propertyKey) {
- return null != propertySource && propertySource.getSecretReferences(algorithmRole).containsKey(propertyKey);
+ private static boolean isSecretReferenceProperty(final WorkflowRequest request, final String algorithmRole, final String propertyKey) {
+ return null != request && request.getSecretReferences(algorithmRole).containsKey(propertyKey);
}
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactPayloadUtils.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactPayloadUtils.java
index ff7c41705cf..0670a6542bd 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactPayloadUtils.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactPayloadUtils.java
@@ -19,8 +19,8 @@ package org.apache.shardingsphere.mcp.support.workflow.service;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import java.util.Map;
@@ -42,11 +42,11 @@ public final class WorkflowArtifactPayloadUtils {
* Create masked workflow artifact payload.
*
* @param snapshot workflow snapshot
- * @param propertySource workflow property source
+ * @param request workflow request
* @return masked workflow artifact payload
*/
- public static Map<String, Object> createArtifactPayload(final WorkflowContextSnapshot snapshot, final WorkflowPropertySource propertySource) {
- return WorkflowArtifactBundle.from(snapshot).toPayload(propertySource, snapshot.getPropertyRequirements());
+ public static Map<String, Object> createArtifactPayload(final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
+ return WorkflowArtifactBundle.from(snapshot).toPayload(request, snapshot.getPropertyRequirements());
}
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanPayloadBuilder.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanPayloadBuilder.java
index 20fe1544959..069851904d6 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanPayloadBuilder.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanPayloadBuilder.java
@@ -20,7 +20,6 @@ package org.apache.shardingsphere.mcp.support.workflow.service;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.shardingsphere.mcp.support.protocol.MCPPayloadFieldNames;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.descriptor.WorkflowToolDescriptors;
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmCandidate;
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
@@ -89,12 +88,12 @@ public final class WorkflowPlanPayloadBuilder {
* Build one workflow-plan payload map with generated artifacts.
*
* @param snapshot workflow snapshot
- * @param propertySource workflow property source
+ * @param request workflow request
* @return workflow-plan payload with generated artifacts
*/
- public static Map<String, Object> buildWithArtifacts(final WorkflowContextSnapshot snapshot, final WorkflowPropertySource propertySource) {
+ public static Map<String, Object> buildWithArtifacts(final WorkflowContextSnapshot snapshot, final WorkflowRequest request) {
Map<String, Object> result = build(snapshot);
- result.putAll(WorkflowArtifactPayloadUtils.createArtifactPayload(snapshot, propertySource));
+ result.putAll(WorkflowArtifactPayloadUtils.createArtifactPayload(snapshot, request));
return result;
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupport.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupport.java
index afd63dc420e..0439623ee13 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupport.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupport.java
@@ -25,8 +25,8 @@ import org.apache.shardingsphere.mcp.support.database.spi.MCPMetadataQueryFacade
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
import org.apache.shardingsphere.mcp.support.workflow.model.ClarifiedIntent;
import org.apache.shardingsphere.mcp.support.workflow.model.InteractionPlan;
+import org.apache.shardingsphere.mcp.support.workflow.model.RuleWorkflowFeatureData;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFeatureData;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowIssue;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowIssueCode;
@@ -73,7 +73,8 @@ public final class WorkflowPlanningSupport {
* @param <T> request type
* @return prepared request
*/
- public <T extends WorkflowRequest> T prepareSnapshot(final WorkflowContextSnapshot snapshot, final WorkflowKind workflowKind, final T request, final WorkflowFeatureData featureData,
+ public <T extends WorkflowRequest> T prepareSnapshot(final WorkflowContextSnapshot snapshot, final WorkflowKind workflowKind, final T request,
+ final RuleWorkflowFeatureData featureData,
final ClarifiedIntent clarifiedIntent, final String summary,
final List<String> interactionSteps, final List<String> validationLayers) {
WorkflowKind existingWorkflowKind = snapshot.getWorkflowKind();
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSecretReferenceUtils.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSecretReferenceUtils.java
index bd022c98ddf..9b0b12a0c5e 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSecretReferenceUtils.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSecretReferenceUtils.java
@@ -19,8 +19,8 @@ package org.apache.shardingsphere.mcp.support.workflow.service;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.model.SecretReferenceValue;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -90,12 +90,12 @@ public final class WorkflowSecretReferenceUtils {
/**
* Create safe summaries for secret references.
*
- * @param propertySource workflow property source
+ * @param request workflow request
* @return safe summaries
*/
- public static List<Map<String, Object>> createSafeSummaries(final WorkflowPropertySource propertySource) {
+ public static List<Map<String, Object>> createSafeSummaries(final WorkflowRequest request) {
List<Map<String, Object>> result = new LinkedList<>();
- propertySource.getSecretReferences().forEach((algorithmRole, references) -> references.forEach(
+ request.getSecretReferences().forEach((algorithmRole, references) -> references.forEach(
(propertyKey, reference) -> result.add(reference.toSafeSummary(algorithmRole, propertyKey))));
return result;
}
@@ -104,15 +104,15 @@ public final class WorkflowSecretReferenceUtils {
* Replace secret reference placeholders with manual placeholders in SQL.
*
* @param sql SQL text
- * @param propertySource workflow property source
+ * @param request workflow request
* @return SQL text with manual placeholders
*/
- public static String replacePlaceholdersWithManualPlaceholders(final String sql, final WorkflowPropertySource propertySource) {
+ public static String replacePlaceholdersWithManualPlaceholders(final String sql, final WorkflowRequest request) {
String result = sql;
- if (null == propertySource) {
+ if (null == request) {
return result;
}
- for (Entry<String, Map<String, SecretReferenceValue>> groupEntry : propertySource.getSecretReferences().entrySet()) {
+ for (Entry<String, Map<String, SecretReferenceValue>> groupEntry : request.getSecretReferences().entrySet()) {
for (String each : groupEntry.getValue().keySet()) {
result = result.replace(SecretReferenceValue.createPlaceholder(groupEntry.getKey(), each), SecretReferenceValue.createManualPlaceholder(groupEntry.getKey(), each));
}
@@ -123,24 +123,24 @@ public final class WorkflowSecretReferenceUtils {
/**
* Judge whether a workflow property source has secret references.
*
- * @param propertySource workflow property source
+ * @param request workflow request
* @return whether secret references exist
*/
- public static boolean hasSecretReferences(final WorkflowPropertySource propertySource) {
- return null != propertySource && propertySource.getSecretReferences().values().stream().anyMatch(each -> !each.isEmpty());
+ public static boolean hasSecretReferences(final WorkflowRequest request) {
+ return null != request && request.getSecretReferences().values().stream().anyMatch(each -> !each.isEmpty());
}
/**
* Judge whether a workflow property source has malformed secret references.
*
- * @param propertySource workflow property source
+ * @param request workflow request
* @return whether malformed secret references exist
*/
- public static boolean hasMalformedSecretReferences(final WorkflowPropertySource propertySource) {
- if (null == propertySource) {
+ public static boolean hasMalformedSecretReferences(final WorkflowRequest request) {
+ if (null == request) {
return false;
}
- return propertySource.getSecretReferences().values().stream().flatMap(each -> each.values().stream()).anyMatch(SecretReferenceValue::isMalformed);
+ return request.getSecretReferences().values().stream().flatMap(each -> each.values().stream()).anyMatch(SecretReferenceValue::isMalformed);
}
/**
@@ -148,18 +148,18 @@ public final class WorkflowSecretReferenceUtils {
*
* @param expectedProperties expected properties
* @param actualProperties actual properties
- * @param propertySource workflow property source
+ * @param request workflow request
* @param algorithmRole algorithm role
* @return whether property maps match
*/
public static boolean matchesManualPlaceholderProperties(final Map<String, String> expectedProperties, final Map<String, String> actualProperties,
- final WorkflowPropertySource propertySource, final String algorithmRole) {
- if (null == propertySource) {
+ final WorkflowRequest request, final String algorithmRole) {
+ if (null == request) {
return expectedProperties.equals(actualProperties);
}
Map<String, String> comparableExpected = new LinkedHashMap<>(expectedProperties);
Map<String, String> comparableActual = new LinkedHashMap<>(actualProperties);
- for (String each : propertySource.getSecretReferences(algorithmRole).keySet()) {
+ for (String each : request.getSecretReferences(algorithmRole).keySet()) {
String expectedPlaceholder = SecretReferenceValue.createPlaceholder(algorithmRole, each);
if (!expectedPlaceholder.equals(expectedProperties.get(each))) {
continue;
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowApplySynchronizationHandler.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowApplySynchronizationHandler.java
deleted file mode 100644
index 98521f8f0b0..00000000000
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowApplySynchronizationHandler.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.workflow.spi;
-
-import org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureExecutionFacade;
-import org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
-import org.apache.shardingsphere.mcp.support.database.spi.MCPMetadataQueryFacade;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
-
-/**
- * Workflow apply synchronization handler.
- */
-@FunctionalInterface
-public interface MCPWorkflowApplySynchronizationHandler {
-
- /**
- * Synchronize workflow state after apply execution.
- *
- * @param snapshot workflow snapshot
- * @param metadataQueryFacade metadata query facade
- * @param queryFacade direct query facade
- * @param executionFacade execution facade
- * @param sessionId session id
- */
- void synchronize(WorkflowContextSnapshot snapshot, MCPMetadataQueryFacade metadataQueryFacade,
- MCPFeatureQueryFacade queryFacade, MCPFeatureExecutionFacade executionFacade, String sessionId);
-}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowRuntimeHandler.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowRuntimeHandler.java
index e7aa6cf3749..fb327d99b16 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowRuntimeHandler.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowRuntimeHandler.java
@@ -17,8 +17,42 @@
package org.apache.shardingsphere.mcp.support.workflow.spi;
+import org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureExecutionFacade;
+import org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
+import org.apache.shardingsphere.mcp.support.database.spi.MCPMetadataQueryFacade;
+import org.apache.shardingsphere.mcp.support.workflow.WorkflowSessionContext;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
+
+import java.util.Map;
+
/**
* Workflow runtime handler for validation and apply synchronization.
*/
-public interface MCPWorkflowRuntimeHandler extends MCPWorkflowValidationHandler, MCPWorkflowApplySynchronizationHandler {
+public interface MCPWorkflowRuntimeHandler {
+
+ /**
+ * Validate one workflow plan.
+ *
+ * @param workflowSessionContext workflow session context
+ * @param metadataQueryFacade metadata query facade
+ * @param queryFacade direct query facade
+ * @param executionFacade execution facade
+ * @param sessionId session id
+ * @param snapshot workflow snapshot
+ * @return validation payload
+ */
+ Map<String, Object> validate(WorkflowSessionContext workflowSessionContext, MCPMetadataQueryFacade metadataQueryFacade,
+ MCPFeatureQueryFacade queryFacade, MCPFeatureExecutionFacade executionFacade, String sessionId, WorkflowContextSnapshot snapshot);
+
+ /**
+ * Synchronize workflow state after apply execution.
+ *
+ * @param snapshot workflow snapshot
+ * @param metadataQueryFacade metadata query facade
+ * @param queryFacade direct query facade
+ * @param executionFacade execution facade
+ * @param sessionId session id
+ */
+ void synchronize(WorkflowContextSnapshot snapshot, MCPMetadataQueryFacade metadataQueryFacade,
+ MCPFeatureQueryFacade queryFacade, MCPFeatureExecutionFacade executionFacade, String sessionId);
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowValidationHandler.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowValidationHandler.java
deleted file mode 100644
index 086d5305763..00000000000
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/MCPWorkflowValidationHandler.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.workflow.spi;
-
-import org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureExecutionFacade;
-import org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
-import org.apache.shardingsphere.mcp.support.database.spi.MCPMetadataQueryFacade;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowSessionContext;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
-
-import java.util.Map;
-
-/**
- * Workflow validation handler.
- */
-@FunctionalInterface
-public interface MCPWorkflowValidationHandler {
-
- /**
- * Validate one workflow plan.
- *
- * @param workflowSessionContext workflow session context
- * @param metadataQueryFacade metadata query facade
- * @param queryFacade direct query facade
- * @param executionFacade execution facade
- * @param sessionId session id
- * @param snapshot workflow snapshot
- * @return validation payload
- */
- Map<String, Object> validate(WorkflowSessionContext workflowSessionContext, MCPMetadataQueryFacade metadataQueryFacade,
- MCPFeatureQueryFacade queryFacade, MCPFeatureExecutionFacade executionFacade, String sessionId, WorkflowContextSnapshot snapshot);
-}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/WorkflowRuntimeDefinition.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/WorkflowRuntimeDefinition.java
index 7233a0b4050..2e41627c676 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/WorkflowRuntimeDefinition.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/spi/WorkflowRuntimeDefinition.java
@@ -17,7 +17,6 @@
package org.apache.shardingsphere.mcp.support.workflow.spi;
-import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowKind;
@@ -25,14 +24,11 @@ import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowKind;
* Workflow runtime definition.
*/
@Getter
-@AllArgsConstructor
public final class WorkflowRuntimeDefinition {
private final WorkflowKind workflowKind;
- private final MCPWorkflowValidationHandler validationHandler;
-
- private final MCPWorkflowApplySynchronizationHandler applySynchronizationHandler;
+ private final MCPWorkflowRuntimeHandler runtimeHandler;
private final MCPWorkflowApplyArtifactValidator applyArtifactValidator;
@@ -44,7 +40,21 @@ public final class WorkflowRuntimeDefinition {
* @param runtimeHandler workflow runtime handler
*/
public WorkflowRuntimeDefinition(final WorkflowKind workflowKind, final MCPWorkflowRuntimeHandler runtimeHandler) {
- this(workflowKind, runtimeHandler, runtimeHandler, createApplyArtifactValidator(runtimeHandler));
+ this(workflowKind, runtimeHandler, createApplyArtifactValidator(runtimeHandler));
+ }
+
+ /**
+ * Create workflow runtime definition with an explicit apply artifact validator.
+ *
+ * @param workflowKind workflow kind
+ * @param runtimeHandler workflow runtime handler
+ * @param applyArtifactValidator apply artifact validator
+ */
+ public WorkflowRuntimeDefinition(final WorkflowKind workflowKind, final MCPWorkflowRuntimeHandler runtimeHandler,
+ final MCPWorkflowApplyArtifactValidator applyArtifactValidator) {
+ this.workflowKind = workflowKind;
+ this.runtimeHandler = runtimeHandler;
+ this.applyArtifactValidator = applyArtifactValidator;
}
private static MCPWorkflowApplyArtifactValidator createApplyArtifactValidator(final MCPWorkflowRuntimeHandler runtimeHandler) {
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/RuntimeDatabaseConfigurationTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/RuntimeDatabaseConfigurationTest.java
index a303b5dc0f1..f0ccae18ace 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/RuntimeDatabaseConfigurationTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/RuntimeDatabaseConfigurationTest.java
@@ -42,15 +42,6 @@ import static org.mockito.Mockito.when;
class RuntimeDatabaseConfigurationTest {
- @Test
- void assertNewRuntimeDatabaseConfiguration() {
- RuntimeDatabaseConfiguration actual = new RuntimeDatabaseConfiguration(RecordingDriver.JDBC_URL, "sa", "pwd", RecordingDriver.class.getName());
- assertThat(actual.getJdbcUrl(), is(RecordingDriver.JDBC_URL));
- assertThat(actual.getUsername(), is("sa"));
- assertThat(actual.getPassword(), is("pwd"));
- assertThat(actual.getDriverClassName(), is(RecordingDriver.class.getName()));
- }
-
@Test
void assertOpenConnectionWithoutCredentials() throws SQLException {
RecordingDriver.reset();
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/query/DatabaseTestDataFactory.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/query/DatabaseTestDataFactory.java
deleted file mode 100644
index da8aca26c69..00000000000
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/query/DatabaseTestDataFactory.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.database.metadata.query;
-
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConfiguration;
-import org.apache.shardingsphere.mcp.support.fixture.SupportDatabaseTypeFactoryMocker;
-
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Types;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-final class DatabaseTestDataFactory {
-
- private static List<DatabaseFixture> createDatabaseMetadata() {
- return List.of(
- new DatabaseFixture("logic_db", "MySQL", "", List.of(
- new SchemaFixture("public", List.of(
- new TableFixture("orders", List.of("order_id", "amount"), List.of("order_idx")),
- new TableFixture("order_items", List.of("item_id"), List.of())),
- List.of(new TableFixture("orders_view", List.of("order_id"), List.of())), List.of()))),
- new DatabaseFixture("runtime_db", "PostgreSQL", "", List.of(
- new SchemaFixture("public", List.of(), List.of(), List.of("order_seq")))),
- new DatabaseFixture("warehouse", "Hive", "", List.of(
- new SchemaFixture("warehouse", List.of(new TableFixture("facts", List.of(), List.of())), List.of(), List.of()))));
- }
-
- static Map<String, RuntimeDatabaseConfiguration> createRuntimeDatabases() {
- Map<String, RuntimeDatabaseConfiguration> result = new LinkedHashMap<>(3, 1F);
- for (DatabaseFixture each : createDatabaseMetadata()) {
- result.put(each.database, createRuntimeDatabaseConfiguration(each));
- }
- return result;
- }
-
- private static RuntimeDatabaseConfiguration createRuntimeDatabaseConfiguration(final DatabaseFixture databaseMetadata) {
- RuntimeDatabaseConfiguration result = mock(RuntimeDatabaseConfiguration.class);
- try {
- when(result.openConnection(databaseMetadata.database)).thenAnswer(invocation -> createConnection(databaseMetadata));
- } catch (final SQLException ex) {
- throw new IllegalStateException(ex);
- }
- return result;
- }
-
- private static Connection createConnection(final DatabaseFixture databaseMetadata) throws SQLException {
- Connection result = mock(Connection.class);
- DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);
- Statement statement = mock(Statement.class);
- when(result.getMetaData()).thenReturn(databaseMetaData);
- when(result.createStatement()).thenReturn(statement);
- when(databaseMetaData.getDatabaseProductVersion()).thenReturn(databaseMetadata.databaseVersion);
- when(databaseMetaData.getURL()).thenReturn(SupportDatabaseTypeFactoryMocker.createJdbcUrl(databaseMetadata.databaseType));
- when(databaseMetaData.getSearchStringEscape()).thenReturn("\\");
- when(databaseMetaData.getTables(nullable(String.class), nullable(String.class), eq("%"), any(String[].class))).thenAnswer(invocation -> {
- String[] tableTypes = invocation.getArgument(3, String[].class);
- return createResultSet("TABLE".equals(tableTypes[0]) ? createTableRows(databaseMetadata) : createViewRows(databaseMetadata));
- });
- when(databaseMetaData.getColumns(nullable(String.class), nullable(String.class), anyString(), eq("%")))
- .thenAnswer(invocation -> createResultSet(createColumnRows(databaseMetadata, invocation.getArgument(2, String.class))));
- when(databaseMetaData.getIndexInfo(nullable(String.class), nullable(String.class), anyString(), eq(false), eq(false)))
- .thenAnswer(invocation -> createResultSet(createIndexRows(databaseMetadata, invocation.getArgument(2, String.class))));
- ResultSet sequenceResultSet = createResultSet(createSequenceRows(databaseMetadata));
- when(statement.executeQuery(anyString())).thenReturn(sequenceResultSet);
- return result;
- }
-
- private static List<Map<String, Object>> createTableRows(final DatabaseFixture databaseMetadata) {
- List<Map<String, Object>> result = new LinkedList<>();
- for (SchemaFixture each : databaseMetadata.schemas) {
- for (TableFixture table : each.tables) {
- result.add(Map.of("TABLE_SCHEM", each.schema, "TABLE_CAT", "", "TABLE_NAME", table.name));
- }
- }
- return result;
- }
-
- private static List<Map<String, Object>> createViewRows(final DatabaseFixture databaseMetadata) {
- List<Map<String, Object>> result = new LinkedList<>();
- for (SchemaFixture each : databaseMetadata.schemas) {
- for (TableFixture view : each.views) {
- result.add(Map.of("TABLE_SCHEM", each.schema, "TABLE_CAT", "", "TABLE_NAME", view.name));
- }
- }
- return result;
- }
-
- private static List<Map<String, Object>> createColumnRows(final DatabaseFixture databaseMetadata, final String objectName) {
- List<Map<String, Object>> result = new LinkedList<>();
- String relationName = "%".equals(objectName) ? "" : unescapePattern(objectName);
- for (SchemaFixture each : databaseMetadata.schemas) {
- for (TableFixture table : each.tables) {
- if (relationName.isEmpty() || table.name.equals(relationName)) {
- for (int i = 0; i < table.columns.size(); i++) {
- result.add(createColumnRow(table.name, table.columns.get(i), i + 1));
- }
- }
- }
- for (TableFixture view : each.views) {
- if (relationName.isEmpty() || view.name.equals(relationName)) {
- for (int i = 0; i < view.columns.size(); i++) {
- result.add(createColumnRow(view.name, view.columns.get(i), i + 1));
- }
- }
- }
- }
- return result;
- }
-
- private static String unescapePattern(final String value) {
- StringBuilder result = new StringBuilder(value.length());
- boolean escaped = false;
- for (char each : value.toCharArray()) {
- if (escaped) {
- result.append(each);
- escaped = false;
- } else if ('\\' == each) {
- escaped = true;
- } else {
- result.append(each);
- }
- }
- return escaped ? result.append('\\').toString() : result.toString();
- }
-
- private static Map<String, Object> createColumnRow(final String relationName, final String columnName, final int ordinalPosition) {
- return Map.of("TABLE_NAME", relationName, "COLUMN_NAME", columnName, "ORDINAL_POSITION", ordinalPosition,
- "DATA_TYPE", Types.INTEGER, "TYPE_NAME", "INT", "NULLABLE", DatabaseMetaData.columnNullable);
- }
-
- private static List<Map<String, Object>> createIndexRows(final DatabaseFixture databaseMetadata, final String tableName) {
- List<Map<String, Object>> result = new LinkedList<>();
- for (SchemaFixture each : databaseMetadata.schemas) {
- for (TableFixture table : each.tables) {
- if (table.name.equals(tableName)) {
- for (String index : table.indexes) {
- result.add(Map.of("INDEX_NAME", index, "TYPE", DatabaseMetaData.tableIndexOther, "NON_UNIQUE", false,
- "ORDINAL_POSITION", 1, "COLUMN_NAME", table.columns.isEmpty() ? "" : table.columns.getFirst()));
- }
- }
- }
- }
- return result;
- }
-
- private static List<Map<String, Object>> createSequenceRows(final DatabaseFixture databaseMetadata) {
- List<Map<String, Object>> result = new LinkedList<>();
- for (SchemaFixture each : databaseMetadata.schemas) {
- for (String sequence : each.sequences) {
- result.add(Map.of("SEQUENCE_SCHEMA", each.schema, "SEQUENCE_NAME", sequence));
- }
- }
- return result;
- }
-
- private static ResultSet createResultSet(final List<Map<String, Object>> rows) throws SQLException {
- ResultSet result = mock(ResultSet.class);
- AtomicInteger rowIndex = new AtomicInteger(-1);
- when(result.next()).thenAnswer(invocation -> rowIndex.incrementAndGet() < rows.size());
- when(result.getString(anyString())).thenAnswer(invocation -> {
- Object value = rows.get(rowIndex.get()).get(invocation.getArgument(0, String.class));
- return null == value ? null : value.toString();
- });
- when(result.getInt(anyString())).thenAnswer(invocation -> getNumber(rows, rowIndex.get(), invocation.getArgument(0, String.class)).intValue());
- when(result.getShort(anyString())).thenAnswer(invocation -> getNumber(rows, rowIndex.get(), invocation.getArgument(0, String.class)).shortValue());
- when(result.getBoolean(anyString())).thenAnswer(invocation -> Boolean.TRUE.equals(rows.get(rowIndex.get()).get(invocation.getArgument(0, String.class))));
- return result;
- }
-
- private static Number getNumber(final List<Map<String, Object>> rows, final int rowIndex, final String columnLabel) {
- Object value = rows.get(rowIndex).get(columnLabel);
- return value instanceof Number ? (Number) value : 0;
- }
-
- @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
- private static final class DatabaseFixture {
-
- private final String database;
-
- private final String databaseType;
-
- private final String databaseVersion;
-
- private final List<SchemaFixture> schemas;
- }
-
- @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
- private static final class SchemaFixture {
-
- private final String schema;
-
- private final List<TableFixture> tables;
-
- private final List<TableFixture> views;
-
- private final List<String> sequences;
- }
-
- @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
- private static final class TableFixture {
-
- private final String name;
-
- private final List<String> columns;
-
- private final List<String> indexes;
- }
-}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/query/MetadataQueryServiceTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/query/MetadataQueryServiceTest.java
index 17681f5f2a7..fa500f5e2fc 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/query/MetadataQueryServiceTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/query/MetadataQueryServiceTest.java
@@ -47,10 +47,18 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.MockedStatic;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
import java.util.List;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;
-import java.sql.Types;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -58,6 +66,10 @@ 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;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
@@ -81,7 +93,7 @@ class MetadataQueryServiceTest {
mockDatabaseType("MySQL", false);
mockDatabaseType("PostgreSQL", true);
mockDatabaseType("Hive", false);
- Map<String, RuntimeDatabaseConfiguration> runtimeDatabases = DatabaseTestDataFactory.createRuntimeDatabases();
+ Map<String, RuntimeDatabaseConfiguration> runtimeDatabases = createRuntimeDatabases();
MCPDatabaseCapabilityProvider databaseCapabilityProvider = new MCPDatabaseCapabilityProvider(runtimeDatabases);
metadataQueryService = new MetadataQueryService(databaseCapabilityProvider, new RequestScopedMetadataContext(runtimeDatabases, databaseCapabilityProvider));
}
@@ -361,4 +373,171 @@ class MetadataQueryServiceTest {
Arguments.of("unsupported sequence", "logic_db", SupportedMCPMetadataObjectType.SEQUENCE, false),
Arguments.of("missing database", "unknown_db", SupportedMCPMetadataObjectType.TABLE, false));
}
+
+ private static Map<String, RuntimeDatabaseConfiguration> createRuntimeDatabases() {
+ Map<String, RuntimeDatabaseConfiguration> result = new LinkedHashMap<>(3, 1F);
+ for (DatabaseFixture each : createDatabaseMetadata()) {
+ result.put(each.database(), createRuntimeDatabaseConfiguration(each));
+ }
+ return result;
+ }
+
+ private static List<DatabaseFixture> createDatabaseMetadata() {
+ return List.of(
+ new DatabaseFixture("logic_db", "MySQL", "", List.of(
+ new SchemaFixture("public", List.of(
+ new TableFixture("orders", List.of("order_id", "amount"), List.of("order_idx")),
+ new TableFixture("order_items", List.of("item_id"), List.of())),
+ List.of(new TableFixture("orders_view", List.of("order_id"), List.of())), List.of()))),
+ new DatabaseFixture("runtime_db", "PostgreSQL", "", List.of(
+ new SchemaFixture("public", List.of(), List.of(), List.of("order_seq")))),
+ new DatabaseFixture("warehouse", "Hive", "", List.of(
+ new SchemaFixture("warehouse", List.of(new TableFixture("facts", List.of(), List.of())), List.of(), List.of()))));
+ }
+
+ private static RuntimeDatabaseConfiguration createRuntimeDatabaseConfiguration(final DatabaseFixture databaseMetadata) {
+ RuntimeDatabaseConfiguration result = mock(RuntimeDatabaseConfiguration.class);
+ try {
+ when(result.openConnection(databaseMetadata.database())).thenAnswer(invocation -> createConnection(databaseMetadata));
+ } catch (final SQLException ex) {
+ throw new IllegalStateException(ex);
+ }
+ return result;
+ }
+
+ private static Connection createConnection(final DatabaseFixture databaseMetadata) throws SQLException {
+ Connection result = mock(Connection.class);
+ DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);
+ Statement statement = mock(Statement.class);
+ when(result.getMetaData()).thenReturn(databaseMetaData);
+ when(result.createStatement()).thenReturn(statement);
+ when(databaseMetaData.getDatabaseProductVersion()).thenReturn(databaseMetadata.databaseVersion());
+ when(databaseMetaData.getURL()).thenReturn(SupportDatabaseTypeFactoryMocker.createJdbcUrl(databaseMetadata.databaseType()));
+ when(databaseMetaData.getSearchStringEscape()).thenReturn("\\");
+ when(databaseMetaData.getTables(nullable(String.class), nullable(String.class), eq("%"), any(String[].class))).thenAnswer(invocation -> {
+ String[] tableTypes = invocation.getArgument(3, String[].class);
+ return createResultSet("TABLE".equals(tableTypes[0]) ? createTableRows(databaseMetadata) : createViewRows(databaseMetadata));
+ });
+ when(databaseMetaData.getColumns(nullable(String.class), nullable(String.class), anyString(), eq("%")))
+ .thenAnswer(invocation -> createResultSet(createColumnRows(databaseMetadata, invocation.getArgument(2, String.class))));
+ when(databaseMetaData.getIndexInfo(nullable(String.class), nullable(String.class), anyString(), eq(false), eq(false)))
+ .thenAnswer(invocation -> createResultSet(createIndexRows(databaseMetadata, invocation.getArgument(2, String.class))));
+ ResultSet sequenceResultSet = createResultSet(createSequenceRows(databaseMetadata));
+ when(statement.executeQuery(anyString())).thenReturn(sequenceResultSet);
+ return result;
+ }
+
+ private static List<Map<String, Object>> createTableRows(final DatabaseFixture databaseMetadata) {
+ List<Map<String, Object>> result = new LinkedList<>();
+ for (SchemaFixture each : databaseMetadata.schemas()) {
+ for (TableFixture table : each.tables()) {
+ result.add(Map.of("TABLE_SCHEM", each.schema(), "TABLE_CAT", "", "TABLE_NAME", table.name()));
+ }
+ }
+ return result;
+ }
+
+ private static List<Map<String, Object>> createViewRows(final DatabaseFixture databaseMetadata) {
+ List<Map<String, Object>> result = new LinkedList<>();
+ for (SchemaFixture each : databaseMetadata.schemas()) {
+ for (TableFixture view : each.views()) {
+ result.add(Map.of("TABLE_SCHEM", each.schema(), "TABLE_CAT", "", "TABLE_NAME", view.name()));
+ }
+ }
+ return result;
+ }
+
+ private static List<Map<String, Object>> createColumnRows(final DatabaseFixture databaseMetadata, final String objectName) {
+ List<Map<String, Object>> result = new LinkedList<>();
+ String relationName = "%".equals(objectName) ? "" : unescapePattern(objectName);
+ for (SchemaFixture each : databaseMetadata.schemas()) {
+ appendColumnRows(result, each.tables(), relationName);
+ appendColumnRows(result, each.views(), relationName);
+ }
+ return result;
+ }
+
+ private static void appendColumnRows(final List<Map<String, Object>> result, final List<TableFixture> relations, final String relationName) {
+ for (TableFixture each : relations) {
+ if (relationName.isEmpty() || each.name().equals(relationName)) {
+ for (int i = 0; i < each.columns().size(); i++) {
+ result.add(createColumnRow(each.name(), each.columns().get(i), i + 1));
+ }
+ }
+ }
+ }
+
+ private static String unescapePattern(final String value) {
+ StringBuilder result = new StringBuilder(value.length());
+ boolean escaped = false;
+ for (char each : value.toCharArray()) {
+ if (escaped) {
+ result.append(each);
+ escaped = false;
+ } else if ('\\' == each) {
+ escaped = true;
+ } else {
+ result.append(each);
+ }
+ }
+ return escaped ? result.append('\\').toString() : result.toString();
+ }
+
+ private static Map<String, Object> createColumnRow(final String relationName, final String columnName, final int ordinalPosition) {
+ return Map.of("TABLE_NAME", relationName, "COLUMN_NAME", columnName, "ORDINAL_POSITION", ordinalPosition,
+ "DATA_TYPE", Types.INTEGER, "TYPE_NAME", "INT", "NULLABLE", DatabaseMetaData.columnNullable);
+ }
+
+ private static List<Map<String, Object>> createIndexRows(final DatabaseFixture databaseMetadata, final String tableName) {
+ List<Map<String, Object>> result = new LinkedList<>();
+ for (SchemaFixture each : databaseMetadata.schemas()) {
+ for (TableFixture table : each.tables()) {
+ if (table.name().equals(tableName)) {
+ for (String index : table.indexes()) {
+ result.add(Map.of("INDEX_NAME", index, "TYPE", DatabaseMetaData.tableIndexOther, "NON_UNIQUE", false,
+ "ORDINAL_POSITION", 1, "COLUMN_NAME", table.columns().isEmpty() ? "" : table.columns().getFirst()));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static List<Map<String, Object>> createSequenceRows(final DatabaseFixture databaseMetadata) {
+ List<Map<String, Object>> result = new LinkedList<>();
+ for (SchemaFixture each : databaseMetadata.schemas()) {
+ for (String sequence : each.sequences()) {
+ result.add(Map.of("SEQUENCE_SCHEMA", each.schema(), "SEQUENCE_NAME", sequence));
+ }
+ }
+ return result;
+ }
+
+ private static ResultSet createResultSet(final List<Map<String, Object>> rows) throws SQLException {
+ ResultSet result = mock(ResultSet.class);
+ AtomicInteger rowIndex = new AtomicInteger(-1);
+ when(result.next()).thenAnswer(invocation -> rowIndex.incrementAndGet() < rows.size());
+ when(result.getString(anyString())).thenAnswer(invocation -> {
+ Object value = rows.get(rowIndex.get()).get(invocation.getArgument(0, String.class));
+ return null == value ? null : value.toString();
+ });
+ when(result.getInt(anyString())).thenAnswer(invocation -> getNumber(rows, rowIndex.get(), invocation.getArgument(0, String.class)).intValue());
+ when(result.getShort(anyString())).thenAnswer(invocation -> getNumber(rows, rowIndex.get(), invocation.getArgument(0, String.class)).shortValue());
+ when(result.getBoolean(anyString())).thenAnswer(invocation -> Boolean.TRUE.equals(rows.get(rowIndex.get()).get(invocation.getArgument(0, String.class))));
+ return result;
+ }
+
+ private static Number getNumber(final List<Map<String, Object>> rows, final int rowIndex, final String columnLabel) {
+ Object value = rows.get(rowIndex).get(columnLabel);
+ return value instanceof Number ? (Number) value : 0;
+ }
+
+ private record DatabaseFixture(String database, String databaseType, String databaseVersion, List<SchemaFixture> schemas) {
+ }
+
+ private record SchemaFixture(String schema, List<TableFixture> tables, List<TableFixture> views, List<String> sequences) {
+ }
+
+ private record TableFixture(String name, List<String> columns, List<String> indexes) {
+ }
}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/diagnostic/MCPDiagnosticCategoryTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/diagnostic/MCPDiagnosticCategoryTest.java
deleted file mode 100644
index 25cf2c534cd..00000000000
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/diagnostic/MCPDiagnosticCategoryTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.diagnostic;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-class MCPDiagnosticCategoryTest {
-
- @Test
- void assertCategories() {
- List<String> actual = List.of(
- MCPDiagnosticCategory.NO_RUNTIME_DATABASE,
- MCPDiagnosticCategory.UNKNOWN_DATABASE,
- MCPDiagnosticCategory.DATABASE_NOT_VISIBLE,
- MCPDiagnosticCategory.SCHEMA_NOT_VISIBLE,
- MCPDiagnosticCategory.OBJECT_NOT_VISIBLE,
- MCPDiagnosticCategory.INSUFFICIENT_PRIVILEGES,
- MCPDiagnosticCategory.EMPTY_SCOPE,
- MCPDiagnosticCategory.NOT_FOUND,
- MCPDiagnosticCategory.ORIGIN_NOT_ALLOWED,
- MCPDiagnosticCategory.SESSION_ATTRIBUTION_MISMATCH,
- MCPDiagnosticCategory.SQL_SYNTAX_ERROR,
- MCPDiagnosticCategory.EXECUTION_TIMEOUT,
- MCPDiagnosticCategory.CONNECTION_INTERRUPTED,
- MCPDiagnosticCategory.UNSUPPORTED_DATABASE_CAPABILITY,
- MCPDiagnosticCategory.QUERY_FAILED);
- assertThat(actual, is(List.of(
- "no_runtime_database",
- "unknown_database",
- "database_not_visible",
- "schema_not_visible",
- "object_not_visible",
- "insufficient_privileges",
- "empty_scope",
- "not_found",
- "origin_not_allowed",
- "session_attribution_mismatch",
- "sql_syntax_error",
- "execution_timeout",
- "connection_interrupted",
- "unsupported_database_capability",
- "query_failed")));
- }
-}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/protocol/payload/MCPMapPayloadTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/protocol/payload/MCPMapPayloadTest.java
deleted file mode 100644
index 7c47c985b5c..00000000000
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/protocol/payload/MCPMapPayloadTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.protocol.payload;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.Map;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.sameInstance;
-
-class MCPMapPayloadTest {
-
- @Test
- void assertToPayload() {
- Map<String, Object> expectedPayload = Map.of("foo_key", "bar_value");
- Map<String, Object> actual = new MCPMapPayload(expectedPayload).toPayload();
- assertThat(actual, sameInstance(expectedPayload));
- }
-}
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
deleted file mode 100644
index 982cacec628..00000000000
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/security/MCPClientSafetyPolicyTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.mcp.support.security;
-
-import org.junit.jupiter.api.Test;
-import java.util.Map;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class MCPClientSafetyPolicyTest {
-
- @Test
- void assertCreateModelFacingPayload() {
- Map<String, Object> actual = MCPClientSafetyPolicy.createModelFacingPayload();
- assertThat(actual.get("identity_scope"), is("mcp_session"));
- assertTrue(String.valueOf(actual.get("transport_scope")).contains("trusted session attribution"));
- assertTrue(String.valueOf(actual.get("external_model_boundary")).contains("never calls external model providers"));
- Map<?, ?> actualRuntimeProtection = (Map<?, ?>) actual.get("runtime_protection");
- Map<?, ?> actualToolCallLimit = (Map<?, ?>) actualRuntimeProtection.get("tool_call_limit");
- assertThat(actualToolCallLimit.get("scope"), is("session"));
- assertThat(actualToolCallLimit.get("property"), is(MCPRuntimeProtectionPolicy.MAX_TOOL_CALLS_PER_SESSION_PROPERTY));
- Map<?, ?> actualSQLExecutionLimits = (Map<?, ?>) actualRuntimeProtection.get("sql_execution_limits");
- assertThat(((Map<?, ?>) actualSQLExecutionLimits.get("max_rows")).get("applied_field"), is("applied_max_rows"));
- assertThat(((Map<?, ?>) actualSQLExecutionLimits.get("timeout_ms")).get("applied_field"), is("applied_timeout_ms"));
- }
-}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowContextSnapshotTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowContextSnapshotTest.java
index 69f6266dca8..8edf75d9ffa 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowContextSnapshotTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/model/WorkflowContextSnapshotTest.java
@@ -67,7 +67,7 @@ class WorkflowContextSnapshotTest {
@Test
void assertClearPlanningStateRemovesTransientArtifacts() {
WorkflowContextSnapshot snapshot = createSnapshot();
- WorkflowFeatureData featureData = snapshot.getFeatureData();
+ RuleWorkflowFeatureData featureData = snapshot.getFeatureData();
assertThat(snapshot.getFeatureData(), is(featureData));
snapshot.clearPlanningState();
assertTrue(snapshot.getIssues().isEmpty());
@@ -93,7 +93,7 @@ class WorkflowContextSnapshotTest {
ClarifiedIntent clarifiedIntent = new ClarifiedIntent();
clarifiedIntent.getClarificationMessages().add("provide schema");
result.setClarifiedIntent(clarifiedIntent);
- result.setFeatureData(new StubWorkflowFeatureData());
+ result.setFeatureData(new RuleWorkflowFeatureData(List.of(Map.of("rule", "encrypt_rule"))));
InteractionPlan interactionPlan = new InteractionPlan();
interactionPlan.setCurrentStep("review");
interactionPlan.getValidationStrategy().put("layers", List.of("rule"));
@@ -120,11 +120,4 @@ class WorkflowContextSnapshotTest {
return (List<String>) source.get(key);
}
- private static final class StubWorkflowFeatureData implements WorkflowFeatureData {
-
- @Override
- public WorkflowFeatureData copy() {
- return new StubWorkflowFeatureData();
- }
- }
}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactBundleTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactBundleTest.java
index 9db12593b55..796e126872f 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactBundleTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactBundleTest.java
@@ -17,15 +17,14 @@
package org.apache.shardingsphere.mcp.support.workflow.service;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
import org.apache.shardingsphere.mcp.support.workflow.model.RuleArtifact;
import org.apache.shardingsphere.mcp.support.workflow.model.SecretReferenceValue;
import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import org.junit.jupiter.api.Test;
import java.util.List;
-import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@@ -36,7 +35,7 @@ class WorkflowArtifactBundleTest {
void assertToExecutableArtifacts() {
WorkflowContextSnapshot snapshot = new WorkflowContextSnapshot();
snapshot.getRuleArtifacts().add(new RuleArtifact("create", "CREATE ENCRYPT RULE t"));
- List<WorkflowArtifactBundle.ExecutableWorkflowArtifact> actual = WorkflowArtifactBundle.from(snapshot).toExecutableArtifacts(role -> Map.of(), List.of());
+ List<WorkflowArtifactBundle.ExecutableWorkflowArtifact> actual = WorkflowArtifactBundle.from(snapshot).toExecutableArtifacts(new WorkflowRequest(), List.of());
assertThat(actual, is(List.of(new WorkflowArtifactBundle.ExecutableWorkflowArtifact("CREATE ENCRYPT RULE t", "CREATE ENCRYPT RULE t"))));
}
@@ -45,7 +44,7 @@ class WorkflowArtifactBundleTest {
WorkflowContextSnapshot snapshot = new WorkflowContextSnapshot();
snapshot.getRuleArtifacts().add(new RuleArtifact("create", "CREATE ENCRYPT RULE t (PROPERTIES('aes-key-value'='primary-secret'))"));
List<WorkflowArtifactBundle.ExecutableWorkflowArtifact> actual = WorkflowArtifactBundle.from(snapshot)
- .toExecutableArtifacts(createPropertySource(), List.of(new AlgorithmPropertyRequirement("primary", "aes-key-value", true, true, "primary", "")));
+ .toExecutableArtifacts(createRequest(), List.of(new AlgorithmPropertyRequirement("primary", "aes-key-value", true, true, "primary", "")));
assertThat(actual.size(), is(1));
assertThat(actual.getFirst().sql(), is("CREATE ENCRYPT RULE t (PROPERTIES('aes-key-value'='primary-secret'))"));
assertThat(actual.getFirst().displaySql(), is("CREATE ENCRYPT RULE t (PROPERTIES('aes-key-value'='******'))"));
@@ -56,28 +55,22 @@ class WorkflowArtifactBundleTest {
WorkflowContextSnapshot snapshot = new WorkflowContextSnapshot();
snapshot.getRuleArtifacts().add(new RuleArtifact("create", "CREATE ENCRYPT RULE t (PROPERTIES('aes-key-value'='secret_reference:primary.aes-key-value'))"));
List<WorkflowArtifactBundle.ExecutableWorkflowArtifact> actual = WorkflowArtifactBundle.from(snapshot)
- .toExecutableArtifacts(createSecretReferencePropertySource(), List.of());
+ .toExecutableArtifacts(createSecretReferenceRequest(), List.of());
assertThat(actual.size(), is(1));
assertThat(actual.getFirst().sql(), is("CREATE ENCRYPT RULE t (PROPERTIES('aes-key-value'='secret_reference:primary.aes-key-value'))"));
assertThat(actual.getFirst().displaySql(), is("CREATE ENCRYPT RULE t (PROPERTIES('aes-key-value'='<SECRET_VALUE_PRIMARY_AES_KEY_VALUE>'))"));
}
- private WorkflowPropertySource createPropertySource() {
- return algorithmRole -> "primary".equals(algorithmRole) ? Map.of("aes-key-value", "primary-secret") : Map.of();
+ private WorkflowRequest createRequest() {
+ WorkflowRequest result = new WorkflowRequest();
+ result.getPrimaryAlgorithmProperties().put("aes-key-value", "primary-secret");
+ return result;
}
- private WorkflowPropertySource createSecretReferencePropertySource() {
- return new WorkflowPropertySource() {
-
- @Override
- public Map<String, String> getAlgorithmProperties(final String algorithmRole) {
- return "primary".equals(algorithmRole) ? Map.of("aes-key-value", "secret_reference:primary.aes-key-value") : Map.of();
- }
-
- @Override
- public Map<String, Map<String, SecretReferenceValue>> getSecretReferences() {
- return Map.of("primary", Map.of("aes-key-value", SecretReferenceValue.create()));
- }
- };
+ private WorkflowRequest createSecretReferenceRequest() {
+ WorkflowRequest result = new WorkflowRequest();
+ result.getPrimaryAlgorithmProperties().put("aes-key-value", "secret_reference:primary.aes-key-value");
+ result.getPrimaryAlgorithmSecretReferences().put("aes-key-value", SecretReferenceValue.create());
+ return result;
}
}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactMaskUtilsTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactMaskUtilsTest.java
index 788a75248cc..5ccae2db2c9 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactMaskUtilsTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowArtifactMaskUtilsTest.java
@@ -17,10 +17,10 @@
package org.apache.shardingsphere.mcp.support.workflow.service;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.model.AlgorithmPropertyRequirement;
import org.apache.shardingsphere.mcp.support.workflow.model.RuleArtifact;
import org.apache.shardingsphere.mcp.support.workflow.model.SecretReferenceValue;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -33,21 +33,21 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class WorkflowArtifactMaskUtilsTest {
- private final WorkflowPropertySource propertySource = createPropertySource();
+ private final WorkflowRequest request = createRequest();
@Test
- void assertCreateMaskedRuleArtifactMapMasksSecretPropertiesAcrossRoles() {
+ void assertCreateMaskedRuleArtifactMapMasksSecretProperties() {
RuleArtifact ruleArtifact = new RuleArtifact("create", "SQL primary-secret 'assist-secret' like-secret");
- Map<String, Object> actualRuleArtifact = WorkflowArtifactMaskUtils.createMaskedRuleArtifactMap(ruleArtifact, propertySource, List.of(
+ Map<String, Object> actualRuleArtifact = WorkflowArtifactMaskUtils.createMaskedRuleArtifactMap(ruleArtifact, request, List.of(
new AlgorithmPropertyRequirement("primary", "aes-key-value", true, true, "primary", ""),
- new AlgorithmPropertyRequirement("assisted_query", "salt", true, true, "assist", ""),
- new AlgorithmPropertyRequirement("like_query", "token", true, true, "like", "")));
+ new AlgorithmPropertyRequirement("primary", "salt", true, true, "assist", ""),
+ new AlgorithmPropertyRequirement("primary", "token", true, true, "like", "")));
assertThat(actualRuleArtifact.get("operation_type"), is("create"));
assertThat(actualRuleArtifact.get("sql"), is("SQL ****** '******' ******"));
Map<?, ?> actualRedaction = (Map<?, ?>) actualRuleArtifact.get("redaction");
assertTrue((Boolean) actualRedaction.get("applied"));
assertThat(actualRedaction.get("redacted_count"), is(3));
- assertThat(actualRedaction.get("redacted_properties"), is(List.of("primary.aes-key-value", "assisted_query.salt", "like_query.token")));
+ assertThat(actualRedaction.get("redacted_properties"), is(List.of("primary.aes-key-value", "primary.salt", "primary.token")));
assertThat(actualRedaction.get("categories"), is(List.of("aes-key-value", "salt", "token")));
assertFalse(actualRedaction.containsKey("secret_reference_summary"));
}
@@ -61,39 +61,29 @@ class WorkflowArtifactMaskUtilsTest {
@Test
void assertMaskSecretReferencePlaceholder() {
- WorkflowPropertySource source = createSecretReferencePropertySource();
- assertThat(WorkflowArtifactMaskUtils.maskSensitiveSql("SQL secret_reference:primary.aes-key-value", source, List.of()), is("SQL <SECRET_VALUE_PRIMARY_AES_KEY_VALUE>"));
+ WorkflowRequest actualRequest = createSecretReferenceRequest();
+ assertThat(WorkflowArtifactMaskUtils.maskSensitiveSql("SQL secret_reference:primary.aes-key-value", actualRequest, List.of()), is("SQL <SECRET_VALUE_PRIMARY_AES_KEY_VALUE>"));
assertThat(WorkflowArtifactMaskUtils.maskPropertyMap(Map.of("aes-key-value", "secret_reference:primary.aes-key-value"), List.of()), is(Map.of("aes-key-value", "******")));
- assertThat(WorkflowArtifactMaskUtils.maskPropertyMap(Map.of("replace-char", "?"), List.of(), source, "primary"), is(Map.of("replace-char", "******")));
- Map<?, ?> actualSummary = WorkflowArtifactMaskUtils.createSecretReferenceSummary(source);
+ assertThat(WorkflowArtifactMaskUtils.maskPropertyMap(Map.of("replace-char", "?"), List.of(), actualRequest, "primary"), is(Map.of("replace-char", "******")));
+ Map<?, ?> actualSummary = WorkflowArtifactMaskUtils.createSecretReferenceSummary(actualRequest);
assertTrue((Boolean) actualSummary.get("required"));
assertThat(actualSummary.get("reference_count"), is(2));
assertThat(actualSummary.get("value_handling"), is("manual_execution"));
assertFalse(String.valueOf(actualSummary).contains("placeholder://"));
}
- private WorkflowPropertySource createPropertySource() {
- Map<String, Map<String, String>> properties = Map.of(
- "primary", Map.of("aes-key-value", "primary-secret"),
- "assisted_query", Map.of("salt", "assist-secret"),
- "like_query", Map.of("token", "like-secret"));
- return algorithmRole -> properties.getOrDefault(algorithmRole, Map.of());
+ private WorkflowRequest createRequest() {
+ WorkflowRequest result = new WorkflowRequest();
+ result.getPrimaryAlgorithmProperties().putAll(Map.of("aes-key-value", "primary-secret", "salt", "assist-secret", "token", "like-secret"));
+ return result;
}
- private WorkflowPropertySource createSecretReferencePropertySource() {
- return new WorkflowPropertySource() {
-
- @Override
- public Map<String, String> getAlgorithmProperties(final String algorithmRole) {
- return Map.of("aes-key-value", "secret_reference:primary.aes-key-value");
- }
-
- @Override
- public Map<String, Map<String, SecretReferenceValue>> getSecretReferences() {
- return Map.of("primary", Map.of(
- "aes-key-value", SecretReferenceValue.create(),
- "replace-char", SecretReferenceValue.create()));
- }
- };
+ private WorkflowRequest createSecretReferenceRequest() {
+ WorkflowRequest result = new WorkflowRequest();
+ result.getPrimaryAlgorithmProperties().put("aes-key-value", "secret_reference:primary.aes-key-value");
+ result.getPrimaryAlgorithmSecretReferences().putAll(Map.of(
+ "aes-key-value", SecretReferenceValue.create(),
+ "replace-char", SecretReferenceValue.create()));
+ return result;
}
}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSecretReferenceUtilsTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSecretReferenceUtilsTest.java
index 89bf5d32e6b..f4d0860c6b7 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSecretReferenceUtilsTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSecretReferenceUtilsTest.java
@@ -17,8 +17,8 @@
package org.apache.shardingsphere.mcp.support.workflow.service;
-import org.apache.shardingsphere.mcp.support.workflow.WorkflowPropertySource;
import org.apache.shardingsphere.mcp.support.workflow.model.SecretReferenceValue;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -60,19 +60,8 @@ class WorkflowSecretReferenceUtilsTest {
@Test
void assertCreateSafeSummaries() {
- WorkflowPropertySource propertySource = new WorkflowPropertySource() {
-
- @Override
- public Map<String, String> getAlgorithmProperties(final String algorithmRole) {
- return Map.of();
- }
-
- @Override
- public Map<String, Map<String, SecretReferenceValue>> getSecretReferences() {
- return Map.of("primary", Map.of("aes-key-value", SecretReferenceValue.create()));
- }
- };
- List<Map<String, Object>> actual = WorkflowSecretReferenceUtils.createSafeSummaries(propertySource);
+ WorkflowRequest request = createSecretReferenceRequest();
+ List<Map<String, Object>> actual = WorkflowSecretReferenceUtils.createSafeSummaries(request);
assertThat(actual.size(), is(1));
assertThat(actual.getFirst().get("label"), is("secret_placeholder:primary.aes-key-value"));
assertFalse(String.valueOf(actual).contains("placeholder://secret-value-1"));
@@ -82,7 +71,7 @@ class WorkflowSecretReferenceUtilsTest {
@Test
void assertReplacePlaceholdersWithManualPlaceholders() {
assertThat(WorkflowSecretReferenceUtils.replacePlaceholdersWithManualPlaceholders(
- "PROPERTIES('aes-key-value'='secret_reference:primary.aes-key-value')", createSecretReferencePropertySource()),
+ "PROPERTIES('aes-key-value'='secret_reference:primary.aes-key-value')", createSecretReferenceRequest()),
is("PROPERTIES('aes-key-value'='<SECRET_VALUE_PRIMARY_AES_KEY_VALUE>')"));
}
@@ -91,7 +80,7 @@ class WorkflowSecretReferenceUtilsTest {
assertTrue(WorkflowSecretReferenceUtils.matchesManualPlaceholderProperties(
Map.of("aes-key-value", "secret_reference:primary.aes-key-value", "digest-algorithm-name", "SHA-256"),
Map.of("aes-key-value", "manually-filled-secret", "digest-algorithm-name", "SHA-256"),
- createSecretReferencePropertySource(), "primary"));
+ createSecretReferenceRequest(), "primary"));
}
@Test
@@ -99,7 +88,7 @@ class WorkflowSecretReferenceUtilsTest {
assertFalse(WorkflowSecretReferenceUtils.matchesManualPlaceholderProperties(
Map.of("aes-key-value", "secret_reference:primary.aes-key-value"),
Map.of("aes-key-value", "secret_reference:primary.aes-key-value"),
- createSecretReferencePropertySource(), "primary"));
+ createSecretReferenceRequest(), "primary"));
}
@Test
@@ -107,7 +96,7 @@ class WorkflowSecretReferenceUtilsTest {
assertFalse(WorkflowSecretReferenceUtils.matchesManualPlaceholderProperties(
Map.of("aes-key-value", "secret_reference:primary.aes-key-value"),
Map.of("aes-key-value", "<SECRET_VALUE_PRIMARY_AES_KEY_VALUE>"),
- createSecretReferencePropertySource(), "primary"));
+ createSecretReferenceRequest(), "primary"));
}
@Test
@@ -115,21 +104,12 @@ class WorkflowSecretReferenceUtilsTest {
assertFalse(WorkflowSecretReferenceUtils.matchesManualPlaceholderProperties(
Map.of("aes-key-value", "secret_reference:primary.aes-key-value"),
Map.of(),
- createSecretReferencePropertySource(), "primary"));
+ createSecretReferenceRequest(), "primary"));
}
- private WorkflowPropertySource createSecretReferencePropertySource() {
- return new WorkflowPropertySource() {
-
- @Override
- public Map<String, String> getAlgorithmProperties(final String algorithmRole) {
- return Map.of();
- }
-
- @Override
- public Map<String, Map<String, SecretReferenceValue>> getSecretReferences() {
- return Map.of("primary", Map.of("aes-key-value", SecretReferenceValue.create()));
- }
- };
+ private WorkflowRequest createSecretReferenceRequest() {
+ WorkflowRequest result = new WorkflowRequest();
+ result.getPrimaryAlgorithmSecretReferences().put("aes-key-value", SecretReferenceValue.create());
+ return result;
}
}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractFunctionalityE2ETest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractFunctionalityE2ETest.java
deleted file mode 100644
index 1aca2065d19..00000000000
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractFunctionalityE2ETest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.test.e2e.mcp.functionality;
-
-import org.apache.shardingsphere.test.e2e.mcp.support.runtime.AbstractConfigBackedRuntimeE2ETest;
-import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionPayloads;
-
-import java.util.List;
-import java.util.Map;
-
-abstract class AbstractFunctionalityE2ETest extends AbstractConfigBackedRuntimeE2ETest {
-
- protected final List<Map<String, Object>> getPayloadItems(final Map<String, Object> payload) {
- return MCPInteractionPayloads.getRequiredObjectList(payload, "items");
- }
-
- protected final List<String> getNestedNames(final Map<String, Object> item, final String nestedKey, final String nameKey) {
- return ((List<?>) item.get(nestedKey)).stream().map(each -> String.valueOf(((Map<?, ?>) each).get(nameKey))).toList();
- }
-}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractMySQLRuntimeE2ETest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractMySQLRuntimeE2ETest.java
index ae62258c6f5..382f2a88be2 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractMySQLRuntimeE2ETest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractMySQLRuntimeE2ETest.java
@@ -131,11 +131,28 @@ abstract class AbstractMySQLRuntimeE2ETest extends AbstractTransportParameterize
}
protected static Stream<Arguments> singleMetadataResourceCases() {
- return FunctionalityTransportCases.singleMetadataResourceCases(LOGICAL_DATABASE_NAME);
+ return Stream.of(
+ Arguments.of("database detail", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME, "database", LOGICAL_DATABASE_NAME),
+ Arguments.of("schema detail", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas/" + LOGICAL_DATABASE_NAME, "schema", LOGICAL_DATABASE_NAME),
+ Arguments.of("table column detail",
+ "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas/" + LOGICAL_DATABASE_NAME + "/tables/orders/columns/status", "column", "status"),
+ Arguments.of("view detail", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas/" + LOGICAL_DATABASE_NAME
+ + "/views/active_orders", "view", "active_orders"),
+ Arguments.of("view column detail", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas/" + LOGICAL_DATABASE_NAME
+ + "/views/active_orders/columns/status", "column", "status"),
+ Arguments.of("index detail", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas/" + LOGICAL_DATABASE_NAME
+ + "/tables/orders/indexes/idx_orders_status", "index", "idx_orders_status"));
}
protected static Stream<Arguments> collectionMetadataResourceCases() {
- return FunctionalityTransportCases.collectionMetadataResourceCases(LOGICAL_DATABASE_NAME);
+ return Stream.of(
+ Arguments.of("schemas list", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas", "schema", List.of(LOGICAL_DATABASE_NAME)),
+ Arguments.of("tables list", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas/" + LOGICAL_DATABASE_NAME + "/tables", "table",
+ List.of("order_items", "orders")),
+ Arguments.of("table columns list", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas/" + LOGICAL_DATABASE_NAME
+ + "/tables/orders/columns", "column", List.of("amount", "order_id", "status")),
+ Arguments.of("view columns list", "shardingsphere://databases/" + LOGICAL_DATABASE_NAME + "/schemas/" + LOGICAL_DATABASE_NAME
+ + "/views/active_orders/columns", "column", List.of("order_id", "status")));
}
protected void assertRecoveryResponse(final Map<String, Object> actual) {
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractTransportParameterizedE2ETest.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractTransportParameterizedE2ETest.java
index ebb0498f960..cea38eb8736 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractTransportParameterizedE2ETest.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/AbstractTransportParameterizedE2ETest.java
@@ -17,18 +17,32 @@
package org.apache.shardingsphere.test.e2e.mcp.functionality;
+import org.apache.shardingsphere.test.e2e.mcp.support.runtime.AbstractConfigBackedRuntimeE2ETest;
import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
+import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionPayloads;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.params.provider.Arguments;
+import java.util.List;
+import java.util.Map;
import java.util.stream.Stream;
-abstract class AbstractTransportParameterizedE2ETest extends AbstractFunctionalityE2ETest {
+abstract class AbstractTransportParameterizedE2ETest extends AbstractConfigBackedRuntimeE2ETest {
private RuntimeTransport transport = RuntimeTransport.HTTP;
protected static Stream<Arguments> allTransportCases() {
- return FunctionalityTransportCases.allTransportCases();
+ return Stream.of(
+ Arguments.of("http", RuntimeTransport.HTTP),
+ Arguments.of("stdio", RuntimeTransport.STDIO));
+ }
+
+ protected final List<Map<String, Object>> getPayloadItems(final Map<String, Object> payload) {
+ return MCPInteractionPayloads.getRequiredObjectList(payload, "items");
+ }
+
+ protected final List<String> getNestedNames(final Map<String, Object> item, final String nestedKey, final String nameKey) {
+ return ((List<?>) item.get(nestedKey)).stream().map(each -> String.valueOf(((Map<?, ?>) each).get(nameKey))).toList();
}
protected final void useTransport(final RuntimeTransport transport) {
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/FunctionalityTransportCases.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/FunctionalityTransportCases.java
deleted file mode 100644
index 43d5796f167..00000000000
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/functionality/FunctionalityTransportCases.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.test.e2e.mcp.functionality;
-
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
-import org.junit.jupiter.params.provider.Arguments;
-
-import java.util.List;
-import java.util.stream.Stream;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-final class FunctionalityTransportCases {
-
- static Stream<Arguments> allTransportCases() {
- return allTransports().map(each -> Arguments.of(getTransportName(each), each));
- }
-
- static Stream<Arguments> singleMetadataResourceCases(final String logicalDatabaseName) {
- return Stream.of(
- Arguments.of("database detail", "shardingsphere://databases/" + logicalDatabaseName, "database", logicalDatabaseName),
- Arguments.of("schema detail", "shardingsphere://databases/" + logicalDatabaseName + "/schemas/" + logicalDatabaseName, "schema", logicalDatabaseName),
- Arguments.of("table column detail",
- "shardingsphere://databases/" + logicalDatabaseName + "/schemas/" + logicalDatabaseName + "/tables/orders/columns/status", "column", "status"),
- Arguments.of("view detail", "shardingsphere://databases/" + logicalDatabaseName + "/schemas/" + logicalDatabaseName + "/views/active_orders", "view", "active_orders"),
- Arguments.of("view column detail",
- "shardingsphere://databases/" + logicalDatabaseName + "/schemas/" + logicalDatabaseName + "/views/active_orders/columns/status", "column", "status"),
- Arguments.of("index detail",
- "shardingsphere://databases/" + logicalDatabaseName + "/schemas/" + logicalDatabaseName + "/tables/orders/indexes/idx_orders_status", "index", "idx_orders_status"));
- }
-
- static Stream<Arguments> collectionMetadataResourceCases(final String logicalDatabaseName) {
- return Stream.of(
- Arguments.of("schemas list", "shardingsphere://databases/" + logicalDatabaseName + "/schemas", "schema", List.of(logicalDatabaseName)),
- Arguments.of("tables list", "shardingsphere://databases/" + logicalDatabaseName + "/schemas/" + logicalDatabaseName + "/tables", "table", List.of("order_items", "orders")),
- Arguments.of("table columns list", "shardingsphere://databases/" + logicalDatabaseName + "/schemas/" + logicalDatabaseName + "/tables/orders/columns", "column",
- List.of("amount", "order_id", "status")),
- Arguments.of("view columns list", "shardingsphere://databases/" + logicalDatabaseName + "/schemas/" + logicalDatabaseName + "/views/active_orders/columns", "column",
- List.of("order_id", "status")));
- }
-
- private static Stream<RuntimeTransport> allTransports() {
- return Stream.of(RuntimeTransport.HTTP, RuntimeTransport.STDIO);
- }
-
- private static String getTransportName(final RuntimeTransport transport) {
- return RuntimeTransport.HTTP == transport ? "http" : "stdio";
- }
-
-}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMConversationRunner.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMConversationRunner.java
index d880d695749..d7006f0fcf0 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMConversationRunner.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMConversationRunner.java
@@ -25,6 +25,7 @@ import org.apache.shardingsphere.test.e2e.mcp.llm.conversation.client.LLMToolCal
import org.apache.shardingsphere.test.e2e.mcp.llm.config.LLME2EConfiguration;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionTraceRecord;
import org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPInteractionClient;
+import org.apache.shardingsphere.infra.util.json.JsonUtils;
import java.io.IOException;
import java.util.LinkedList;
@@ -67,15 +68,12 @@ public final class LLMConversationRunner {
private final LLMMCPSafetyValidator safetyValidator = new LLMMCPSafetyValidator();
- private final LLMMCPActionExecutor actionExecutor;
-
public LLMConversationRunner(final int maxTurns, final LLMChatModelClient llmChatClient, final MCPInteractionClient mcpInteractionClient,
final String modelName) {
this.maxTurns = maxTurns;
this.llmChatClient = llmChatClient;
this.mcpInteractionClient = mcpInteractionClient;
this.modelName = modelName;
- actionExecutor = new LLMMCPActionExecutor(mcpInteractionClient);
}
/**
@@ -176,7 +174,7 @@ public final class LLMConversationRunner {
long startTime = System.currentTimeMillis();
Map<String, Object> response;
try {
- response = actionExecutor.executeSafely(each.getName(), arguments);
+ response = executeAction(each.getName(), arguments);
} catch (final IllegalArgumentException ex) {
artifacts.addTrace(MCPInteractionTraceRecord.createInvalidAction(
artifacts.nextSequence(), modelTurn, TOOL_CALL_KIND, each.getName(), arguments, "invalid_tool_arguments"));
@@ -190,7 +188,7 @@ public final class LLMConversationRunner {
artifacts.addTrace(new MCPInteractionTraceRecord(
artifacts.nextSequence(), modelTurn, getActionKind(each.getName()), MCPInteractionTraceRecord.MODEL_TOOL_CALL_ORIGIN,
each.getName(), getTraceArguments(each.getName(), arguments), response, true, latencyMillis));
- messages.add(LLMChatMessage.tool(each.getId(), LLMMCPModelFacingToolResponseFormatter.format(response)));
+ messages.add(LLMChatMessage.tool(each.getId(), JsonUtils.toJsonString(response)));
}
return Optional.empty();
}
@@ -199,6 +197,24 @@ public final class LLMConversationRunner {
return READ_RESOURCE_TOOL_NAME.equals(toolName) ? RESOURCE_READ_KIND : TOOL_CALL_KIND;
}
+ private Map<String, Object> executeAction(final String actionName, final Map<String, Object> arguments) throws InterruptedException {
+ try {
+ return READ_RESOURCE_TOOL_NAME.equals(actionName)
+ ? mcpInteractionClient.readResource(getRequiredResourceUri(arguments))
+ : mcpInteractionClient.call(actionName, arguments);
+ } catch (final IOException | IllegalStateException ex) {
+ throw new IllegalStateException(String.format("MCP action `%s` failed: %s", actionName, ex.getMessage()), ex);
+ }
+ }
+
+ private String getRequiredResourceUri(final Map<String, Object> arguments) {
+ Object value = arguments.get("uri");
+ if (!(value instanceof String) || ((String) value).trim().isEmpty()) {
+ throw new IllegalArgumentException("Resource URI is required.");
+ }
+ return ((String) value).trim();
+ }
+
private Map<String, Object> getTraceArguments(final String toolName, final Map<String, Object> arguments) {
return READ_RESOURCE_TOOL_NAME.equals(toolName)
? Map.of("uri", Objects.toString(arguments.get("uri"), "").trim())
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
deleted file mode 100644
index 6cbd15a6dc2..00000000000
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPActionExecutor.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.test.e2e.mcp.llm.conversation;
-
-import lombok.AccessLevel;
-import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPInteractionClient;
-
-import java.io.IOException;
-import java.util.Map;
-
-@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
-final class LLMMCPActionExecutor {
-
- private final MCPInteractionClient mcpInteractionClient;
-
- Map<String, Object> executeSafely(final String actionName, final Map<String, Object> args) throws InterruptedException {
- try {
- return LLMConversationRunner.READ_RESOURCE_TOOL_NAME.equals(actionName)
- ? mcpInteractionClient.readResource(getRequiredResourceUri(args))
- : mcpInteractionClient.call(actionName, args);
- } catch (final IOException | IllegalStateException ex) {
- throw new IllegalStateException(String.format("MCP action `%s` failed: %s", actionName, ex.getMessage()), ex);
- }
- }
-
- private String getRequiredResourceUri(final Map<String, Object> args) {
- Object value = args.get("uri");
- if (!(value instanceof String) || ((String) value).trim().isEmpty()) {
- throw new IllegalArgumentException("Resource URI is required.");
- }
- return ((String) value).trim();
- }
-}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPJsonValues.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPJsonValues.java
index 30ea13c0e35..9733aa6139d 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPJsonValues.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPJsonValues.java
@@ -22,9 +22,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.infra.util.json.JsonUtils;
-
-import java.util.List;
import java.util.Map;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@@ -41,22 +38,16 @@ final class LLMMCPJsonValues {
}
}
- static List<List<Object>> castToRows(final Object value) {
- return JsonUtils.fromJsonString(JsonUtils.toJsonString(value), new TypeReference<>() {
- });
- }
-
+ @SuppressWarnings("unchecked")
static Map<String, Object> castToMap(final Object value) {
- return JsonUtils.fromJsonString(JsonUtils.toJsonString(value), new TypeReference<>() {
- });
- }
-
- static <T> List<T> castToList(final Object value) {
- if (null == value) {
- return List.of();
+ if (!(value instanceof Map)) {
+ throw new IllegalArgumentException("Expected a JSON object.");
+ }
+ Map<?, ?> result = (Map<?, ?>) value;
+ if (!result.keySet().stream().allMatch(String.class::isInstance)) {
+ throw new IllegalArgumentException("Expected a JSON object with string keys.");
}
- return JsonUtils.fromJsonString(JsonUtils.toJsonString(value), new TypeReference<>() {
- });
+ return (Map<String, Object>) result;
}
}
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
deleted file mode 100644
index a748d08aeeb..00000000000
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatter.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.test.e2e.mcp.llm.conversation;
-
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.infra.util.json.JsonUtils;
-import org.apache.shardingsphere.mcp.support.protocol.MCPModelFacingPayloadContract;
-import org.apache.shardingsphere.mcp.support.protocol.MCPPayloadFieldNames;
-import org.apache.shardingsphere.mcp.support.protocol.MCPResponseMode;
-
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-final class LLMMCPModelFacingToolResponseFormatter {
-
- private static final int COMPACT_ITEM_LIMIT = 5;
-
- private static final List<String> GENERAL_FIELD_NAMES = List.of(
- "response_mode", "error_code", "error_id", "summary", "message", "recovery_category", "result_kind", "status", "statement_type", "normalized_sql", "rows", "row_objects",
- "returned_row_count", "plan_id", "workflow_resource");
-
- private static final List<String> POST_ACTION_FIELD_NAMES = List.of(
- "completion", "count", "total_match_count", "truncated", "large_result_guidance", "search_context");
-
- static String format(final Map<String, Object> response) {
- Map<String, Object> result = new LinkedHashMap<>(16, 1F);
- copyFields(response, result, GENERAL_FIELD_NAMES);
- copyCompactArtifactPackage(response, result, "manual_artifact_package");
- copyCompactArtifactList(response, result, "exported_artifacts");
- copyModelCriticalFields(response, result);
- copyNextActions(response, result);
- copyFields(response, result, POST_ACTION_FIELD_NAMES);
- copyToolList(response, result);
- copyCompactResourceList(response, result, "resources", "uri");
- copyCompactResourceList(response, result, "resourceTemplates", "uriTemplate");
- copyPromptList(response, result);
- copyPromptMessages(response, result);
- copyCompactItems(response, result);
- copyCompactRecovery(response, result);
- return JsonUtils.toJsonString(result.isEmpty() ? response : result);
- }
-
- private static void copyCompactResourceList(final Map<String, Object> source, final Map<String, Object> target,
- final String fieldName, final String uriFieldName) {
- List<Map<String, Object>> resources = LLMMCPJsonValues.castToList(source.get(fieldName));
- if (!resources.isEmpty()) {
- List<Map<String, Object>> compactResources = new LinkedList<>();
- for (Map<String, Object> each : resources) {
- Map<String, Object> compactResource = new LinkedHashMap<>(8, 1F);
- copyIfPresent(each, compactResource, uriFieldName);
- copyIfPresent(each, compactResource, "name");
- copyIfPresent(each, compactResource, "title");
- copyIfPresent(each, compactResource, "mimeType");
- compactResources.add(compactResource.isEmpty() ? each : compactResource);
- }
- target.put(fieldName, compactResources);
- }
- }
-
- private static void copyToolList(final Map<String, Object> source, final Map<String, Object> target) {
- List<Map<String, Object>> tools = LLMMCPJsonValues.castToList(source.get("tools"));
- if (tools.isEmpty()) {
- return;
- }
- List<Map<String, Object>> compactTools = new LinkedList<>();
- for (Map<String, Object> each : tools) {
- Map<String, Object> compactTool = new LinkedHashMap<>(2, 1F);
- copyIfPresent(each, compactTool, "name");
- copyIfPresent(each, compactTool, "title");
- if (!compactTool.isEmpty()) {
- compactTools.add(compactTool);
- }
- }
- target.put("tools", compactTools);
- }
-
- private static void copyFields(final Map<String, Object> source, final Map<String, Object> target, final Collection<String> fieldNames) {
- for (String each : fieldNames) {
- copyIfPresent(source, target, each);
- }
- }
-
- private static void copyIfPresent(final Map<String, Object> source, final Map<String, Object> target, final String fieldName) {
- if (source.containsKey(fieldName)) {
- target.put(fieldName, source.get(fieldName));
- }
- }
-
- private static void copyModelCriticalFields(final Map<String, Object> source, final Map<String, Object> target) {
- for (String each : MCPModelFacingPayloadContract.getModelCriticalFieldNames()) {
- if (!MCPPayloadFieldNames.NEXT_ACTIONS.equals(each) && !MCPPayloadFieldNames.RECOVERY.equals(each)) {
- copyIfPresent(source, target, each);
- }
- }
- }
-
- private static void copyCompactItems(final Map<String, Object> source, final Map<String, Object> target) {
- List<Map<String, Object>> items = LLMMCPJsonValues.castToList(source.get("items"));
- if (items.isEmpty()) {
- return;
- }
- List<Map<String, Object>> compactItems = new LinkedList<>();
- int visibleItemCount = MCPResponseMode.SEARCH.equals(source.get("response_mode")) ? items.size() : Math.min(items.size(), COMPACT_ITEM_LIMIT);
- for (Map<String, Object> each : items.subList(0, visibleItemCount)) {
- Map<String, Object> compactItem = new LinkedHashMap<>(8, 1F);
- copyIfPresent(each, compactItem, "database");
- copyIfPresent(each, compactItem, "schema");
- copyIfPresent(each, compactItem, "objectType");
- copyIfPresent(each, compactItem, "table");
- copyIfPresent(each, compactItem, "view");
- copyIfPresent(each, compactItem, "column");
- copyIfPresent(each, compactItem, "name");
- copyIfPresent(each, compactItem, "resource");
- compactItems.add(compactItem.isEmpty() ? each : compactItem);
- }
- target.put("items", compactItems);
- }
-
- private static void copyPromptList(final Map<String, Object> source, final Map<String, Object> target) {
- List<Map<String, Object>> prompts = LLMMCPJsonValues.castToList(source.get("prompts"));
- if (prompts.isEmpty()) {
- return;
- }
- List<String> names = new LinkedList<>();
- for (Map<String, Object> each : prompts) {
- String name = Objects.toString(each.get("name"), "").trim();
- if (!name.isEmpty()) {
- names.add(name);
- }
- }
- target.put("prompts", names);
- }
-
- private static void copyPromptMessages(final Map<String, Object> source, final Map<String, Object> target) {
- copyIfPresent(source, target, "description");
- 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) {
- List<Map<String, Object>> artifacts = LLMMCPJsonValues.castToList(source.get(fieldName));
- if (artifacts.isEmpty()) {
- return;
- }
- List<Map<String, Object>> summaries = new LinkedList<>();
- for (Map<String, Object> each : artifacts.subList(0, Math.min(artifacts.size(), 3))) {
- Map<String, Object> summary = new LinkedHashMap<>(4, 1F);
- putArtifactCount(each, summary, "distsql_artifacts", "distsql_artifact_count");
- if (!summary.isEmpty()) {
- summaries.add(summary);
- }
- }
- if (!summaries.isEmpty()) {
- target.put(fieldName, summaries);
- }
- }
-
- private static void copyCompactArtifactPackage(final Map<String, Object> source, final Map<String, Object> target, final String fieldName) {
- Object value = source.get(fieldName);
- if (!(value instanceof Map)) {
- return;
- }
- Map<String, Object> artifactPackage = LLMMCPJsonValues.castToMap(value);
- Map<String, Object> summary = new LinkedHashMap<>(1, 1F);
- putArtifactCount(artifactPackage, summary, "distsql_artifacts", "distsql_artifact_count");
- if (!summary.isEmpty()) {
- target.put(fieldName, summary);
- }
- }
-
- private static void putArtifactCount(final Map<String, Object> source, final Map<String, Object> target, final String sourceFieldName, final String targetFieldName) {
- List<Object> artifacts = LLMMCPJsonValues.castToList(source.get(sourceFieldName));
- if (!artifacts.isEmpty()) {
- target.put(targetFieldName, artifacts.size());
- }
- }
-
- private static void copyCompactRecovery(final Map<String, Object> source, final Map<String, Object> target) {
- if (!source.containsKey("recovery")) {
- return;
- }
- Map<String, Object> recovery = LLMMCPJsonValues.castToMap(source.get("recovery"));
- if (recovery.isEmpty()) {
- return;
- }
- Map<String, Object> compactRecovery = new LinkedHashMap<>(8, 1F);
- copyIfPresent(recovery, compactRecovery, "recovery_category");
- copyIfPresent(recovery, compactRecovery, "category");
- copyIfPresent(recovery, compactRecovery, "model_action");
- copyIfPresent(recovery, compactRecovery, "plan_id");
- copyIfPresent(recovery, compactRecovery, "completion_first");
- copyIfPresent(recovery, compactRecovery, "suggested_arguments");
- copyModelCriticalFields(recovery, compactRecovery);
- target.put("recovery", compactRecovery);
- }
-
- private static void copyNextActions(final Map<String, Object> source, final Map<String, Object> target) {
- List<Map<String, Object>> nextActions = LLMMCPJsonValues.castToList(source.get(MCPPayloadFieldNames.NEXT_ACTIONS));
- if (!nextActions.isEmpty()) {
- target.put(MCPPayloadFieldNames.NEXT_ACTIONS, nextActions);
- }
- }
-}
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/client/LLMChatModelClient.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/client/LLMChatModelClient.java
index f2ac6780a15..c7315e32e68 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/client/LLMChatModelClient.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/client/LLMChatModelClient.java
@@ -42,10 +42,6 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public final class LLMChatModelClient {
- private static final int COMPLETION_MAX_TOKENS = 512;
-
- private static final int READINESS_MAX_TOKENS = 64;
-
private final LLME2EConfiguration config;
private final HttpClient httpClient;
@@ -88,7 +84,7 @@ public final class LLMChatModelClient {
*/
public LLMChatCompletion complete(final List<LLMChatMessage> messages, final List<Map<String, Object>> tools,
final String toolChoice, final boolean jsonResponse) throws IOException, InterruptedException {
- HttpResponse<String> response = sendCompletionRequest(createCompletionRequestPayload(messages, tools, toolChoice, jsonResponse, COMPLETION_MAX_TOKENS), config.getRequestTimeoutSeconds());
+ HttpResponse<String> response = sendCompletionRequest(createCompletionRequestPayload(messages, tools, toolChoice, jsonResponse), config.getRequestTimeoutSeconds());
if (200 != response.statusCode()) {
throw new IllegalStateException(String.format("Model completion request failed with status %d%s.", response.statusCode(), createErrorCodeSuffix(response.body())));
}
@@ -101,7 +97,7 @@ public final class LLMChatModelClient {
}
private Map<String, Object> createCompletionRequestPayload(final List<LLMChatMessage> messages, final List<Map<String, Object>> tools,
- final String toolChoice, final boolean jsonResponse, final int maxTokens) {
+ final String toolChoice, final boolean jsonResponse) {
Map<String, Object> requestPayload = new LinkedHashMap<>(16, 1F);
requestPayload.put("model", config.getModelName());
requestPayload.put("messages", createMessages(messages));
@@ -109,7 +105,6 @@ public final class LLMChatModelClient {
requestPayload.put("temperature", 0);
requestPayload.put("seed", 1);
requestPayload.put("reasoning_effort", "none");
- requestPayload.put("max_tokens", maxTokens);
if (!tools.isEmpty()) {
requestPayload.put("tools", tools);
}
@@ -143,7 +138,7 @@ public final class LLMChatModelClient {
HttpResponse<String> sendReadinessCompletionRequest(final List<LLMChatMessage> messages, final List<Map<String, Object>> tools,
final String toolChoice, final boolean jsonResponse) throws IOException, InterruptedException {
- return sendCompletionRequest(createCompletionRequestPayload(messages, tools, toolChoice, jsonResponse, READINESS_MAX_TOKENS),
+ return sendCompletionRequest(createCompletionRequestPayload(messages, tools, toolChoice, jsonResponse),
Math.min(config.getRequestTimeoutSeconds(), config.getReadyTimeoutSeconds()));
}
@@ -213,16 +208,21 @@ public final class LLMChatModelClient {
if (null == value) {
return List.of();
}
- return JsonUtils.fromJsonString(JsonUtils.toJsonString(value), new TypeReference<>() {
- });
+ Preconditions.checkState(value instanceof List, "Expected a JSON array, but got `%s`.", value.getClass().getSimpleName());
+ List<?> values = (List<?>) value;
+ Preconditions.checkState(values.stream().allMatch(Map.class::isInstance), "Expected a JSON array of objects.");
+ return values.stream().map(this::castToMap).toList();
}
+ @SuppressWarnings("unchecked")
private Map<String, Object> castToMap(final Object value) {
if (null == value) {
return Map.of();
}
- return JsonUtils.fromJsonString(JsonUtils.toJsonString(value), new TypeReference<>() {
- });
+ Preconditions.checkState(value instanceof Map, "Expected a JSON object, but got `%s`.", value.getClass().getSimpleName());
+ Map<?, ?> result = (Map<?, ?>) value;
+ Preconditions.checkState(result.keySet().stream().allMatch(String.class::isInstance), "Expected a JSON object with string keys.");
+ return (Map<String, Object>) result;
}
private Map<String, Object> parseJsonObject(final String responseBody, final String errorMessage) {
diff --git a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/fixture/LLMRuntimeSupport.java b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/fixture/LLMRuntimeSupport.java
index 95a36024cbf..e4e243fb94c 100644
--- a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/fixture/LLMRuntimeSupport.java
+++ b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/fixture/LLMRuntimeSupport.java
@@ -179,7 +179,7 @@ public final class LLMRuntimeSupport {
.withExposedPorts(SERVER_PORT)
.withCommand("--host", "0.0.0.0", "--port", String.valueOf(SERVER_PORT), "-m", config.getModelMetadata().getContainerPath(), "--alias", config.getModelName(),
"--jinja", "--reasoning", "auto", "--reasoning-format", "deepseek", "--reasoning-budget", "0", "--chat-template-kwargs", "{\"enable_thinking\":false}",
- "--api-key", config.getApiKey(), "--no-ui", "-n", "512", "--parallel", "1", "-c", String.valueOf(CONTEXT_WINDOW_TOKENS),
+ "--api-key", config.getApiKey(), "--no-ui", "--parallel", "1", "-c", String.valueOf(CONTEXT_WINDOW_TOKENS),
"-b", "256", "-ub", "128", "--cache-ram", "0", "--no-cache-prompt")
.waitingFor(Wait.forListeningPort())
.withStartupTimeout(Duration.ofMinutes(5));