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;
+ }
}