You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/05/31 04:07:07 UTC

(shardingsphere) branch master updated: Refactor MCPToolElicitationHandler (#38763)

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 eb9d330a332 Refactor MCPToolElicitationHandler (#38763)
eb9d330a332 is described below

commit eb9d330a332091c2a5c5758f6c1cc5e80d1a0465
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Sun May 31 12:07:01 2026 +0800

    Refactor MCPToolElicitationHandler (#38763)
    
    * Refactor MCPToolElicitationHandler
    
    * Refactor MCPToolElicitationHandler
    
    * Refactor MCPToolElicitationHandler
    
    * Refactor MCPToolElicitationHandler
    
    * Refactor MCPToolElicitationHandler
    
    * Refactor MCPToolElicitationHandler
---
 .../tool/MCPClientElicitationCapabilities.java     |  2 +-
 .../tool/MCPToolClarificationPolicy.java           | 12 ++--
 .../MCPToolElicitationFallbackResponseFactory.java |  3 +-
 .../capability/tool/MCPToolElicitationHandler.java | 82 +++++++++-------------
 .../tool/MCPToolSpecificationFactory.java          | 10 +--
 5 files changed, 49 insertions(+), 60 deletions(-)

diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPClientElicitationCapabilities.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPClientElicitationCapabilities.java
index eebdc80d6b3..a482011de16 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPClientElicitationCapabilities.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPClientElicitationCapabilities.java
@@ -26,8 +26,8 @@ import lombok.RequiredArgsConstructor;
 /**
  * MCP client elicitation capabilities.
  */
-@Getter
 @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+@Getter
 public final class MCPClientElicitationCapabilities {
     
     private final boolean formModeSupported;
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolClarificationPolicy.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolClarificationPolicy.java
index 370816fd287..0cfed9778bf 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolClarificationPolicy.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolClarificationPolicy.java
@@ -52,14 +52,14 @@ final class MCPToolClarificationPolicy {
     private static final List<String> SENSITIVE_FIELD_NAME_MARKERS = List.of(
             "password", "passwd", "passphrase", "secret", "token", "accesstoken", "apikey", "privatekey", "credential", "card", "cvv", "payment", "key");
     
-    boolean requiresPlanningClarification(final MCPToolDescriptor toolDescriptor, final Map<String, Object> payload) {
+    boolean requiresPlanningClarification(final MCPToolDescriptor descriptor, final Map<String, Object> payload) {
         Object clarificationQuestions = payload.get(MCPPayloadFieldNames.CLARIFICATION_QUESTIONS);
-        return MCPDescriptorCatalogIndex.findToolRuntimeDescriptor(toolDescriptor.getName())
+        return MCPDescriptorCatalogIndex.findToolRuntimeDescriptor(descriptor.getName())
                 .map(optional -> PLANNING_WORKFLOW_ROLE.equals(optional.getWorkflowRole())).orElse(false)
                 && clarificationQuestions instanceof List<?> questions && !questions.isEmpty();
     }
     
-    Optional<ClarificationForm> createClarificationForm(final Map<String, Object> payload, final MCPToolDescriptor toolDescriptor) {
+    Optional<ClarificationForm> createClarificationForm(final Map<String, Object> payload, final MCPToolDescriptor descriptor) {
         String planId = getPlanId(payload).trim();
         if (planId.isEmpty()) {
             return Optional.empty();
@@ -76,7 +76,7 @@ final class MCPToolClarificationPolicy {
             if (!(each instanceof Map<?, ?> question) || isSensitiveClarificationQuestion(question)) {
                 return Optional.empty();
             }
-            Optional<ArgumentBinding> binding = createArgumentBinding(question, toolDescriptor, questionIndex);
+            Optional<ArgumentBinding> binding = createArgumentBinding(question, descriptor, questionIndex);
             if (binding.isEmpty()) {
                 return Optional.empty();
             }
@@ -102,13 +102,13 @@ final class MCPToolClarificationPolicy {
         return false;
     }
     
-    private Optional<ArgumentBinding> createArgumentBinding(final Map<?, ?> question, final MCPToolDescriptor toolDescriptor, final int questionIndex) {
+    private Optional<ArgumentBinding> createArgumentBinding(final Map<?, ?> question, final MCPToolDescriptor descriptor, final int questionIndex) {
         String field = getField(question);
         if (field.isEmpty()) {
             return Optional.empty();
         }
         String formPropertyName = FORM_PROPERTY_PREFIX + questionIndex;
-        return findArgumentBinding(field, toolDescriptor, formPropertyName, getInputType(question), getAllowedValues(question));
+        return findArgumentBinding(field, descriptor, formPropertyName, getInputType(question), getAllowedValues(question));
     }
     
     private boolean isSensitiveClarificationQuestion(final Map<?, ?> question) {
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackResponseFactory.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackResponseFactory.java
index 46b8d3f0412..da99b452d62 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackResponseFactory.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackResponseFactory.java
@@ -52,8 +52,7 @@ public final class MCPToolElicitationFallbackResponseFactory {
      * @param clientCapabilities client elicitation capabilities
      * @return MCP response
      */
-    public MCPResponse create(final Map<String, Object> payload, final MCPToolElicitationFallbackReason fallbackReason,
-                              final MCPClientElicitationCapabilities clientCapabilities) {
+    public MCPResponse create(final Map<String, Object> payload, final MCPToolElicitationFallbackReason fallbackReason, final MCPClientElicitationCapabilities clientCapabilities) {
         Map<String, Object> result = new LinkedHashMap<>(payload);
         if (clarificationPolicy.hasSensitiveClarificationQuestions(payload)) {
             result.put(MCPPayloadFieldNames.CLARIFICATION_QUESTIONS, createSanitizedClarificationQuestions(payload));
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationHandler.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationHandler.java
index 14d6c031041..5b8748a839e 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationHandler.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationHandler.java
@@ -56,60 +56,32 @@ final class MCPToolElicitationHandler {
     
     private final MCPToolElicitationFallbackResponseFactory fallbackResponseFactory = new MCPToolElicitationFallbackResponseFactory();
     
-    boolean shouldHandle(final MCPToolDescriptor toolDescriptor, final Map<String, Object> payload) {
-        return clarificationPolicy.requiresPlanningClarification(toolDescriptor, payload);
+    boolean shouldHandle(final MCPToolDescriptor descriptor, final Map<String, Object> payload) {
+        return clarificationPolicy.requiresPlanningClarification(descriptor, payload);
     }
     
-    MCPResponse handle(final McpSyncServerExchange exchange, final MCPToolDefinition toolDefinition, final Map<String, Object> arguments,
-                       final MCPResponse fallbackResponse, final Map<String, Object> payload) {
-        MCPToolDescriptor toolDescriptor = toolDefinition.getDescriptor();
+    Optional<MCPResponse> handle(final McpSyncServerExchange exchange, final MCPToolDefinition definition, final Map<String, Object> arguments, final Map<String, Object> payload) {
         MCPClientElicitationCapabilities clientCapabilities = MCPClientElicitationCapabilities.from(exchange);
-        Optional<MCPToolClarificationPolicy.ClarificationForm> clarificationForm = clarificationPolicy.createClarificationForm(payload, toolDescriptor);
+        Optional<MCPToolClarificationPolicy.ClarificationForm> clarificationForm = clarificationPolicy.createClarificationForm(payload, definition.getDescriptor());
         if (clarificationForm.isEmpty()) {
-            return createFallbackResponse(payload, getUnavailableFormFallbackReason(payload, clientCapabilities), clientCapabilities);
+            return Optional.of(fallbackResponseFactory.create(payload, getUnavailableFormFallbackReason(payload, clientCapabilities), clientCapabilities));
         }
         if (!clientCapabilities.isFormModeSupported()) {
-            return createFallbackResponse(payload, MCPToolElicitationFallbackReason.CLIENT_UNSUPPORTED, clientCapabilities);
+            return Optional.of(fallbackResponseFactory.create(payload, MCPToolElicitationFallbackReason.CLIENT_UNSUPPORTED, clientCapabilities));
         }
         if (!STDIO_TRANSPORT.equals(activeTransport)) {
-            return createFallbackResponse(payload, MCPToolElicitationFallbackReason.REMOTE_IDENTITY_REQUIRED, clientCapabilities);
+            return Optional.of(fallbackResponseFactory.create(payload, MCPToolElicitationFallbackReason.REMOTE_IDENTITY_REQUIRED, clientCapabilities));
         }
-        FormContinuationContext continuationContext = createContinuationContext(exchange, toolDescriptor, arguments, clarificationForm.get());
+        FormContinuationContext continuationContext = new FormContinuationContext(
+                definition.getDescriptor().getName(), exchange.sessionId(), clarificationForm.get().planId(), arguments.hashCode(), clock.instant().plus(FORM_CONTINUATION_TTL),
+                UUID.randomUUID().toString());
         McpSchema.ElicitResult elicitedResult;
         try {
-            elicitedResult = exchange.createElicitation(createElicitRequest(toolDescriptor.getName(), clarificationForm.get(), continuationContext.formRequestId()));
+            elicitedResult = exchange.createElicitation(createElicitRequest(definition.getDescriptor().getName(), clarificationForm.get(), continuationContext.formRequestId()));
         } catch (final McpError | IllegalStateException | UnsupportedOperationException ignored) {
-            return createFallbackResponse(payload, MCPToolElicitationFallbackReason.ELICITATION_FAILED, clientCapabilities);
-        }
-        return continueOrFallback(exchange, toolDefinition, arguments, fallbackResponse, payload, clarificationForm.get(), continuationContext, elicitedResult, clientCapabilities);
-    }
-    
-    private MCPResponse continueOrFallback(final McpSyncServerExchange exchange, final MCPToolDefinition toolDefinition, final Map<String, Object> arguments,
-                                           final MCPResponse fallbackResponse, final Map<String, Object> payload,
-                                           final MCPToolClarificationPolicy.ClarificationForm clarificationForm,
-                                           final FormContinuationContext continuationContext, final McpSchema.ElicitResult elicitedResult,
-                                           final MCPClientElicitationCapabilities clientCapabilities) {
-        if (null == elicitedResult || null == elicitedResult.action()) {
-            return createFallbackResponse(payload, MCPToolElicitationFallbackReason.MALFORMED_ELICITATION_RESULT, clientCapabilities);
-        }
-        if (McpSchema.ElicitResult.Action.ACCEPT != elicitedResult.action()) {
-            return fallbackResponse;
+            return Optional.of(fallbackResponseFactory.create(payload, MCPToolElicitationFallbackReason.ELICITATION_FAILED, clientCapabilities));
         }
-        if (null == elicitedResult.content()) {
-            return createFallbackResponse(payload, MCPToolElicitationFallbackReason.MALFORMED_ELICITATION_RESULT, clientCapabilities);
-        }
-        if (!continuationContext.isActive(activeTransport, clock, exchange, toolDefinition.getDescriptor(), arguments, clarificationForm)) {
-            return createFallbackResponse(payload, MCPToolElicitationFallbackReason.STALE_ELICITATION, clientCapabilities);
-        }
-        if (!clarificationPolicy.isValidElicitedContent(clarificationForm, elicitedResult.content())) {
-            return createFallbackResponse(payload, MCPToolElicitationFallbackReason.INVALID_ELICITED_CONTENT, clientCapabilities);
-        }
-        return toolController.handle(exchange.sessionId(), toolDefinition, clarificationPolicy.mergeArguments(arguments, clarificationForm, elicitedResult.content()));
-    }
-    
-    private MCPResponse createFallbackResponse(final Map<String, Object> payload, final MCPToolElicitationFallbackReason fallbackReason,
-                                               final MCPClientElicitationCapabilities clientCapabilities) {
-        return fallbackResponseFactory.create(payload, fallbackReason, clientCapabilities);
+        return continueOrFallback(exchange, definition, arguments, payload, clarificationForm.get(), continuationContext, elicitedResult, clientCapabilities);
     }
     
     private MCPToolElicitationFallbackReason getUnavailableFormFallbackReason(final Map<String, Object> payload, final MCPClientElicitationCapabilities clientCapabilities) {
@@ -121,12 +93,6 @@ final class MCPToolElicitationHandler {
                 : MCPToolElicitationFallbackReason.AMBIGUOUS_FIELD_BINDING;
     }
     
-    private FormContinuationContext createContinuationContext(final McpSyncServerExchange exchange, final MCPToolDescriptor toolDescriptor, final Map<String, Object> arguments,
-                                                              final MCPToolClarificationPolicy.ClarificationForm clarificationForm) {
-        return new FormContinuationContext(toolDescriptor.getName(), exchange.sessionId(), clarificationForm.planId(), arguments.hashCode(), clock.instant().plus(FORM_CONTINUATION_TTL),
-                UUID.randomUUID().toString());
-    }
-    
     private McpSchema.ElicitRequest createElicitRequest(final String toolName, final MCPToolClarificationPolicy.ClarificationForm clarificationForm, final String formRequestId) {
         return McpSchema.ElicitRequest.builder()
                 .message(String.format("Provide missing ShardingSphere workflow inputs for `%s`.", toolName))
@@ -142,6 +108,28 @@ final class MCPToolElicitationHandler {
                 MCPShardingSphereMetadataKeys.FORM_REQUEST_ID, formRequestId);
     }
     
+    private Optional<MCPResponse> continueOrFallback(final McpSyncServerExchange exchange, final MCPToolDefinition toolDefinition, final Map<String, Object> arguments,
+                                                     final Map<String, Object> payload, final MCPToolClarificationPolicy.ClarificationForm clarificationForm,
+                                                     final FormContinuationContext continuationContext, final McpSchema.ElicitResult elicitedResult,
+                                                     final MCPClientElicitationCapabilities clientCapabilities) {
+        if (null == elicitedResult || null == elicitedResult.action()) {
+            return Optional.of(fallbackResponseFactory.create(payload, MCPToolElicitationFallbackReason.MALFORMED_ELICITATION_RESULT, clientCapabilities));
+        }
+        if (McpSchema.ElicitResult.Action.ACCEPT != elicitedResult.action()) {
+            return Optional.empty();
+        }
+        if (null == elicitedResult.content()) {
+            return Optional.of(fallbackResponseFactory.create(payload, MCPToolElicitationFallbackReason.MALFORMED_ELICITATION_RESULT, clientCapabilities));
+        }
+        if (!continuationContext.isActive(activeTransport, clock, exchange, toolDefinition.getDescriptor(), arguments, clarificationForm)) {
+            return Optional.of(fallbackResponseFactory.create(payload, MCPToolElicitationFallbackReason.STALE_ELICITATION, clientCapabilities));
+        }
+        if (!clarificationPolicy.isValidElicitedContent(clarificationForm, elicitedResult.content())) {
+            return Optional.of(fallbackResponseFactory.create(payload, MCPToolElicitationFallbackReason.INVALID_ELICITED_CONTENT, clientCapabilities));
+        }
+        return Optional.of(toolController.handle(exchange.sessionId(), toolDefinition, clarificationPolicy.mergeArguments(arguments, clarificationForm, elicitedResult.content())));
+    }
+    
     private record FormContinuationContext(String toolName, String sessionId, String planId, int argumentsHashCode, Instant expiresAt, String formRequestId) {
 
         private boolean isActive(final String activeTransport, final Clock clock, final McpSyncServerExchange exchange, final MCPToolDescriptor toolDescriptor,
diff --git a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationFactory.java b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationFactory.java
index b80016f152e..cefa7b13d77 100644
--- a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationFactory.java
+++ b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationFactory.java
@@ -95,12 +95,14 @@ public final class MCPToolSpecificationFactory {
             Map<String, Object> arguments = Optional.ofNullable(request.arguments()).orElse(Collections.emptyMap());
             MCPToolDefinition definition = ToolDefinitionRegistry.getToolDefinition(request.name());
             MCPResponse response = controller.handle(exchange.sessionId(), definition, arguments);
-            Map<String, Object> payload = response.toPayload();
-            return elicitationHandler.shouldHandle(definition.getDescriptor(), payload)
-                    ? callToolResultFactory.create(definition.getDescriptor(), elicitationHandler.handle(exchange, definition, arguments, response, payload))
-                    : callToolResultFactory.create(definition.getDescriptor(), response);
+            return callToolResultFactory.create(definition.getDescriptor(), getMcpResponse(exchange, response, definition, arguments));
         } catch (final UnsupportedToolException ignored) {
             throw MCPTransportErrorFactory.createError(new UnsupportedToolException(request.name()));
         }
     }
+    
+    private MCPResponse getMcpResponse(final McpSyncServerExchange exchange, final MCPResponse response, final MCPToolDefinition definition, final Map<String, Object> arguments) {
+        Map<String, Object> payload = response.toPayload();
+        return elicitationHandler.shouldHandle(definition.getDescriptor(), payload) ? elicitationHandler.handle(exchange, definition, arguments, payload).orElse(response) : response;
+    }
 }