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 5c5270057ca Simplify tool elicitation handler (#38740)
5c5270057ca is described below

commit 5c5270057cad39b8c252a1199b3f5959c3063bd2
Author: Liang Zhang <[email protected]>
AuthorDate: Thu May 28 12:51:48 2026 +0800

    Simplify tool elicitation handler (#38740)
    
    Refactor MCP tool elicitation handling by extracting client capability
    detection and fallback response construction into dedicated public types.
    Restore ArgumentBinding semantic helper usage, merge elicitation workflow
    coverage into the real specification factory test, and add focused tests for
    new public contracts.
    
    Also document helper extraction boundaries in AGENTS.md.
---
 AGENTS.md                                          |  20 +-
 .../tool/MCPClientElicitationCapabilities.java     |  51 +++
 .../tool/MCPToolClarificationPolicy.java           |   7 +-
 .../tool/MCPToolElicitationFallbackReason.java     |  63 ++++
 .../MCPToolElicitationFallbackResponseFactory.java | 106 ++++++
 .../capability/tool/MCPToolElicitationHandler.java | 164 ++-------
 .../tool/MCPClientElicitationCapabilitiesTest.java |  85 +++++
 .../tool/MCPToolClarificationPolicyTest.java       |  35 ++
 .../tool/MCPToolElicitationFallbackReasonTest.java |  88 +++++
 ...ToolElicitationFallbackResponseFactoryTest.java |  78 ++++
 ...MCPToolSpecificationElicitationFactoryTest.java | 397 ---------------------
 .../tool/MCPToolSpecificationFactoryTest.java      | 364 +++++++++++++++++++
 12 files changed, 921 insertions(+), 537 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md
index 77a31843edc..2c4197a4f84 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -18,6 +18,15 @@ This guide is written **for AI coding agents only**. Follow 
it literally; improv
 - **Architecture**: follow SOLID, DRY, separation of concerns, and YAGNI 
(build only what you need).
 - **Code Quality**:
     - Use clear naming and reasonable abstractions.
+    - Do not introduce package-private top-level helper types by default.
+      Keep very small, single-owner state or continuation helpers as private 
nested types, but avoid accumulating multiple nested collaborators inside one 
class.
+      When a helper has cohesive behavior, multiple callers, direct test 
value, or enough logic to distract from the owner class, split it into a public 
top-level type with a clear contract and direct tests.
+      If neither private nor public fits, pause before coding and explain why.
+    - Every new public production type must have direct, focused tests.
+      Broad workflow tests do not replace public contract tests unless they 
explicitly exercise that public type's behavior.
+    - New internal abstractions must reduce cognitive complexity instead of 
merely wrapping branches in more types.
+      For simple internal two-path flows, avoid marker interfaces, multi-type 
result hierarchies, or extra DTO-style helpers.
+      Add them only when they define a stable boundary, keep owner classes 
readable, or remove meaningful duplicated logic.
     - Delete unused code; when changing functionality, remove legacy 
compatibility shims.
     - Keep variable declarations adjacent to first use; if a value must be 
retained, declare it `final` to satisfy Checkstyle 
VariableDeclarationUsageDistance.
     - Single-use local variables must be inlined by default; keep a local 
variable only when it is reused (for stubbing/verification/assertions) or 
materially improves readability.
@@ -43,7 +52,9 @@ This guide is written **for AI coding agents only**. Follow 
it literally; improv
 - **Test-Driven**: design for testability, ensure unit-test coverage, and keep 
background unit tests under 60s to avoid job stalls.
 - **Quality Assurance**: run static checks, formatting, and code reviews.
 - **Checkstyle Gate**: do not hand off code with Checkstyle/Spotless 
failures—run the relevant module check locally and fix before completion.
-- **Formatting Gate**: after code changes, format only with `./mvnw 
spotless:apply -Pcheck -T1C`, then check style with `./mvnw checkstyle:check 
-Pcheck -T1C`; do not use any other formatting method.
+- **Formatting Gate**: after code or documentation changes, format only with 
`./mvnw spotless:apply -Pcheck -T1C`, then check style with `./mvnw 
checkstyle:check -Pcheck -T1C`; do not use any other formatting method.
+  Spotless must run after the last file-changing action and before 
Checkstyle/tests in the final handoff sequence.
+  If any file is edited, generated, moved, or manually whitespace-cleaned 
after Spotless, rerun Spotless before replying.
 - **Continuous Verification**: rely on automated tests and integration 
validation.
 - **Test Naming Simplicity**: keep test names concise and scenario-focused 
(avoid “ReturnsXXX”/overly wordy or AI-like phrasing); describe the scenario 
directly.
 - **Coverage Discipline**: follow the dedicated coverage & branch checklist 
before coding when coverage targets are stated.
@@ -120,6 +131,8 @@ Dangerous operation detected! Operation type: [specific 
action] Scope of impact:
 - **Execution discipline:** inspect existing code before edits; keep changes 
minimal; default to mocks and SPI loaders; keep variable declarations near 
first use and mark retained values `final`; inline single-use locals by default 
unless reuse/readability justifies retention; delete dead code and avoid 
placeholders/TODOs.
 - **AGENTS.md maintenance:** do not add or update a `Session Notes` section in 
`AGENTS.md`. Keep task-specific notes in the active conversation, issue, or PR; 
only stable project-level rules may be generalized into this file.
 - **Post-task self-check (before replying):** confirm all instructions were 
honored; verify no placeholders/unused code; ensure Checkstyle/Spotless gates 
for touched modules are satisfied or explain why not run and what to run; list 
commands with exit codes; call out risks and follow-ups; complete all 
applicable checks before replying and do not rely on users to find missed rule 
violations.
+- **End-of-task format/style gate:** for any task that edits files, run 
`./mvnw spotless:apply -Pcheck -T1C` after the final edit, then run `./mvnw 
checkstyle:check -Pcheck -T1C` when production, test, or project-rule files are 
touched.
+  Do not perform manual formatting or whitespace cleanup after the final 
Spotless run; if a later cleanup is required, repeat Spotless and then 
Checkstyle before the final response.
 - **Final response template:** include intent/why, changed files with paths, 
rationale per file/section, commands run (with exit codes), verification 
status, and remaining risks/next actions (if tests skipped, state reason and 
the exact command to run); include a concise self-check result statement 
confirming final clean status after fixes.
 
 ## Final Self-Iteration Gate
@@ -228,8 +241,9 @@ Always state which topology, registry, and engine versions 
(e.g., MySQL 5.7 vs 8
 ## Verification & Commands
 - Core commands: `./mvnw clean install -B -T1C -Pcheck` (full build), `./mvnw 
test -pl <module>[-am]` (scoped unit tests), `./mvnw -pl <module> -DskipITs 
-Dspotless.skip=true -Dtest=ClassName test` (fast verification), `./mvnw -pl 
proxy -am -DskipTests package` (proxy packaging/perf smoke).
 - Coverage: when tests change or targets demand it, run `./mvnw test 
jacoco:check@jacoco-check -Pcoverage-check` or scoped `-pl <module> -am 
-Djacoco.skip=false test jacoco:report`; pair with the Coverage & Branch 
Checklist.
-- Format: after code changes, run `./mvnw spotless:apply -Pcheck -T1C`; do not 
use any other formatting method.
-- Style: after code changes and formatting, run `./mvnw checkstyle:check 
-Pcheck -T1C`.
+- Format: after code or documentation changes, run `./mvnw spotless:apply 
-Pcheck -T1C`; do not use any other formatting method.
+  This must be repeated after the last file-changing action before handoff.
+- Style: after formatting, run `./mvnw checkstyle:check -Pcheck -T1C` when 
production, test, or project-rule files are touched.
 - Scoped defaults: prefer module-scoped runs over whole-repo builds; include 
`-Dsurefire.failIfNoSpecifiedTests=false` when targeting specific tests.
 - Testing ground rules: JUnit 5 + Mockito, `ClassNameTest` naming, 
Arrange–Act–Assert, mock external systems/time/network, reset static caches, 
and reuse swappers/helpers for complex configs.
 - API bans: if a user forbids a tool/assertion, add it to the plan, avoid it 
during implementation, and cite verification searches (e.g., `rg assertEquals`) 
in the final report.
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
new file mode 100644
index 00000000000..eebdc80d6b3
--- /dev/null
+++ 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPClientElicitationCapabilities.java
@@ -0,0 +1,51 @@
+/*
+ * 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.server.McpSyncServerExchange;
+import io.modelcontextprotocol.spec.McpSchema;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * MCP client elicitation capabilities.
+ */
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public final class MCPClientElicitationCapabilities {
+    
+    private final boolean formModeSupported;
+    
+    private final boolean urlModeSupported;
+    
+    /**
+     * Create client elicitation capabilities from server exchange.
+     *
+     * @param exchange MCP sync server exchange
+     * @return client elicitation capabilities
+     */
+    public static MCPClientElicitationCapabilities from(final 
McpSyncServerExchange exchange) {
+        McpSchema.ClientCapabilities clientCapabilities = 
exchange.getClientCapabilities();
+        if (null == clientCapabilities || null == 
clientCapabilities.elicitation()) {
+            return new MCPClientElicitationCapabilities(false, false);
+        }
+        McpSchema.ClientCapabilities.Elicitation elicitation = 
clientCapabilities.elicitation();
+        return new MCPClientElicitationCapabilities(null != elicitation.form() 
|| null == elicitation.url(), null != elicitation.url());
+    }
+}
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 d730e6cab4a..87d2b68c93a 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
@@ -81,15 +81,12 @@ final class MCPToolClarificationPolicy {
                 return Optional.empty();
             }
             ArgumentBinding argumentBinding = binding.get();
-            if (fieldBindings.containsKey(argumentBinding.formPropertyName())) 
{
-                return Optional.empty();
-            }
             properties.put(argumentBinding.formPropertyName(), 
createRequestedPropertySchema(question, argumentBinding));
             required.add(argumentBinding.formPropertyName());
             fieldBindings.put(argumentBinding.formPropertyName(), 
argumentBinding);
             questionIndex++;
         }
-        return properties.isEmpty() ? Optional.empty() : Optional.of(new 
ClarificationForm(createObjectSchema(properties, required), fieldBindings, 
planId));
+        return Optional.of(new 
ClarificationForm(createObjectSchema(properties, required), fieldBindings, 
planId));
     }
     
     boolean hasSensitiveClarificationQuestions(final Map<String, Object> 
payload) {
@@ -140,7 +137,7 @@ final class MCPToolClarificationPolicy {
         return value.replaceAll(CAMEL_CASE_SEPARATOR_PATTERN, "$1 
$2").toLowerCase(Locale.ENGLISH).replaceAll(NON_ALPHANUMERIC_PATTERN, "");
     }
     
-    String getPlanId(final Map<String, Object> payload) {
+    private String getPlanId(final Map<String, Object> payload) {
         return Objects.toString(payload.get(WorkflowFieldNames.PLAN_ID), "");
     }
     
diff --git 
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackReason.java
 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackReason.java
new file mode 100644
index 00000000000..7a80a06a190
--- /dev/null
+++ 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackReason.java
@@ -0,0 +1,63 @@
+/*
+ * 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 lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * MCP tool elicitation fallback reason.
+ */
+@Getter
+@RequiredArgsConstructor
+public enum MCPToolElicitationFallbackReason {
+    
+    CLIENT_UNSUPPORTED("client_unsupported", "structured_fallback"),
+    
+    REMOTE_IDENTITY_REQUIRED("remote_identity_required", 
"structured_fallback"),
+    
+    MISSING_PLAN_ID("missing_plan_id", "structured_fallback"),
+    
+    SENSITIVE_FORM_BLOCKED("sensitive_form_blocked", "url_fallback"),
+    
+    URL_MODE_NOT_IMPLEMENTED("url_mode_not_implemented", "url_fallback"),
+    
+    AMBIGUOUS_FIELD_BINDING("ambiguous_field_binding", "structured_fallback"),
+    
+    ELICITATION_FAILED("elicitation_failed", "structured_fallback"),
+    
+    MALFORMED_ELICITATION_RESULT("malformed_elicitation_result", 
"structured_fallback"),
+    
+    INVALID_ELICITED_CONTENT("invalid_elicited_content", 
"structured_fallback"),
+    
+    STALE_ELICITATION("stale_elicitation", "structured_fallback");
+    
+    private final String value;
+    
+    private final String selectedInteraction;
+    
+    /**
+     * Adjust fallback reason according to client capabilities.
+     *
+     * @param clientCapabilities client elicitation capabilities
+     * @return fallback reason
+     */
+    public MCPToolElicitationFallbackReason withClientCapabilities(final 
MCPClientElicitationCapabilities clientCapabilities) {
+        return SENSITIVE_FORM_BLOCKED == this && 
clientCapabilities.isUrlModeSupported() ? URL_MODE_NOT_IMPLEMENTED : this;
+    }
+}
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
new file mode 100644
index 00000000000..46b8d3f0412
--- /dev/null
+++ 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackResponseFactory.java
@@ -0,0 +1,106 @@
+/*
+ * 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 org.apache.shardingsphere.mcp.api.protocol.response.MCPResponse;
+import org.apache.shardingsphere.mcp.support.protocol.MCPPayloadFieldNames;
+import org.apache.shardingsphere.mcp.support.protocol.response.MCPMapResponse;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * MCP tool elicitation fallback response factory.
+ */
+public final class MCPToolElicitationFallbackResponseFactory {
+    
+    private static final String ELICITATION_SUPPORT_FIELD = 
"elicitation_support";
+    
+    private static final String FALLBACK_REASON_FIELD = "fallback_reason";
+    
+    private static final String FORM_MODE_FIELD = "form_mode";
+    
+    private static final String URL_MODE_FIELD = "url_mode";
+    
+    private static final String SELECTED_INTERACTION_FIELD = 
"selected_interaction";
+    
+    private final MCPToolClarificationPolicy clarificationPolicy = new 
MCPToolClarificationPolicy();
+    
+    /**
+     * Create fallback response.
+     *
+     * @param payload original payload
+     * @param fallbackReason fallback reason
+     * @param clientCapabilities client elicitation capabilities
+     * @return MCP response
+     */
+    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));
+            result.put(MCPPayloadFieldNames.NEXT_ACTIONS, 
createSensitiveNextActions());
+        }
+        result.put(ELICITATION_SUPPORT_FIELD, 
createElicitationSupportPayload(clientCapabilities, 
fallbackReason.getSelectedInteraction()));
+        result.put(FALLBACK_REASON_FIELD, fallbackReason.getValue());
+        return new MCPMapResponse(result);
+    }
+    
+    private List<Map<String, Object>> 
createSanitizedClarificationQuestions(final Map<String, Object> payload) {
+        Object clarificationQuestions = 
payload.get(MCPPayloadFieldNames.CLARIFICATION_QUESTIONS);
+        if (!(clarificationQuestions instanceof List<?> questions)) {
+            return List.of();
+        }
+        List<Map<String, Object>> result = new LinkedList<>();
+        for (Object each : questions) {
+            if (each instanceof Map<?, ?> question) {
+                result.add(createSanitizedClarificationQuestion(question));
+            }
+        }
+        return result;
+    }
+    
+    private Map<String, Object> createSanitizedClarificationQuestion(final 
Map<?, ?> question) {
+        Map<String, Object> result = new LinkedHashMap<>(4, 1F);
+        result.put(MCPPayloadFieldNames.FIELD, 
Objects.toString(question.get(MCPPayloadFieldNames.FIELD), ""));
+        result.put(MCPPayloadFieldNames.INPUT_TYPE, "secret");
+        result.put(MCPPayloadFieldNames.SECRET, true);
+        result.put(MCPPayloadFieldNames.MESSAGE, "Sensitive input must be 
provided through configured secure channels before continuing the same 
planner.");
+        return result;
+    }
+    
+    private List<Map<String, Object>> createSensitiveNextActions() {
+        Map<String, Object> result = new LinkedHashMap<>(4, 1F);
+        result.put("order", 1);
+        result.put("type", "terminal");
+        result.put("title", "Collect sensitive inputs through configured 
secure channels.");
+        result.put(MCPPayloadFieldNames.REASON, "MCP form elicitation is 
limited to non-sensitive STDIO continuations; URL mode is not implemented in 
this release.");
+        return List.of(result);
+    }
+    
+    private Map<String, Object> createElicitationSupportPayload(final 
MCPClientElicitationCapabilities clientCapabilities, final String 
selectedInteraction) {
+        Map<String, Object> result = new LinkedHashMap<>(3, 1F);
+        result.put(FORM_MODE_FIELD, clientCapabilities.isFormModeSupported());
+        result.put(URL_MODE_FIELD, clientCapabilities.isUrlModeSupported());
+        result.put(SELECTED_INTERACTION_FIELD, selectedInteraction);
+        return result;
+    }
+}
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 55ce709b117..14d6c031041 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
@@ -26,15 +26,11 @@ import 
org.apache.shardingsphere.mcp.api.tool.descriptor.MCPToolDescriptor;
 import org.apache.shardingsphere.mcp.core.tool.MCPToolController;
 import org.apache.shardingsphere.mcp.core.tool.handler.MCPToolDefinition;
 import 
org.apache.shardingsphere.mcp.support.descriptor.MCPShardingSphereMetadataKeys;
-import org.apache.shardingsphere.mcp.support.protocol.MCPPayloadFieldNames;
-import org.apache.shardingsphere.mcp.support.protocol.response.MCPMapResponse;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
 
 import java.time.Clock;
 import java.time.Duration;
 import java.time.Instant;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -50,40 +46,6 @@ final class MCPToolElicitationHandler {
     
     private static final Duration FORM_CONTINUATION_TTL = 
Duration.ofMinutes(10L);
     
-    private static final String ELICITATION_SUPPORT_FIELD = 
"elicitation_support";
-    
-    private static final String FALLBACK_REASON_FIELD = "fallback_reason";
-    
-    private static final String FORM_MODE_FIELD = "form_mode";
-    
-    private static final String URL_MODE_FIELD = "url_mode";
-    
-    private static final String SELECTED_INTERACTION_FIELD = 
"selected_interaction";
-    
-    private static final String STRUCTURED_FALLBACK_INTERACTION = 
"structured_fallback";
-    
-    private static final String URL_FALLBACK_INTERACTION = "url_fallback";
-    
-    private static final String CLIENT_UNSUPPORTED_REASON = 
"client_unsupported";
-    
-    private static final String REMOTE_IDENTITY_REQUIRED_REASON = 
"remote_identity_required";
-    
-    private static final String MISSING_PLAN_ID_REASON = "missing_plan_id";
-    
-    private static final String SENSITIVE_FORM_BLOCKED_REASON = 
"sensitive_form_blocked";
-    
-    private static final String URL_MODE_NOT_IMPLEMENTED_REASON = 
"url_mode_not_implemented";
-    
-    private static final String AMBIGUOUS_FIELD_BINDING_REASON = 
"ambiguous_field_binding";
-    
-    private static final String ELICITATION_FAILED_REASON = 
"elicitation_failed";
-    
-    private static final String MALFORMED_ELICITATION_RESULT_REASON = 
"malformed_elicitation_result";
-    
-    private static final String INVALID_ELICITED_CONTENT_REASON = 
"invalid_elicited_content";
-    
-    private static final String STALE_ELICITATION_REASON = "stale_elicitation";
-    
     private final MCPToolController toolController;
     
     private final String activeTransport;
@@ -92,6 +54,8 @@ final class MCPToolElicitationHandler {
     
     private final MCPToolClarificationPolicy clarificationPolicy = new 
MCPToolClarificationPolicy();
     
+    private final MCPToolElicitationFallbackResponseFactory 
fallbackResponseFactory = new MCPToolElicitationFallbackResponseFactory();
+    
     boolean shouldHandle(final MCPToolDescriptor toolDescriptor, final 
Map<String, Object> payload) {
         return 
clarificationPolicy.requiresPlanningClarification(toolDescriptor, payload);
     }
@@ -99,122 +63,62 @@ final class MCPToolElicitationHandler {
     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();
-        ClientElicitationSupport clientSupport = 
getClientElicitationSupport(exchange);
+        MCPClientElicitationCapabilities clientCapabilities = 
MCPClientElicitationCapabilities.from(exchange);
         Optional<MCPToolClarificationPolicy.ClarificationForm> 
clarificationForm = clarificationPolicy.createClarificationForm(payload, 
toolDescriptor);
         if (clarificationForm.isEmpty()) {
-            return createFallbackResponse(payload, 
determineUnavailableFormReason(payload, clientSupport), clientSupport);
+            return createFallbackResponse(payload, 
getUnavailableFormFallbackReason(payload, clientCapabilities), 
clientCapabilities);
         }
-        if (!clientSupport.supportsFormMode()) {
-            return createFallbackResponse(payload, CLIENT_UNSUPPORTED_REASON, 
clientSupport);
+        if (!clientCapabilities.isFormModeSupported()) {
+            return createFallbackResponse(payload, 
MCPToolElicitationFallbackReason.CLIENT_UNSUPPORTED, clientCapabilities);
         }
         if (!STDIO_TRANSPORT.equals(activeTransport)) {
-            return createFallbackResponse(payload, 
REMOTE_IDENTITY_REQUIRED_REASON, clientSupport);
+            return createFallbackResponse(payload, 
MCPToolElicitationFallbackReason.REMOTE_IDENTITY_REQUIRED, clientCapabilities);
         }
         FormContinuationContext continuationContext = 
createContinuationContext(exchange, toolDescriptor, arguments, 
clarificationForm.get());
         McpSchema.ElicitResult elicitedResult;
         try {
             elicitedResult = 
exchange.createElicitation(createElicitRequest(toolDescriptor.getName(), 
clarificationForm.get(), continuationContext.formRequestId()));
         } catch (final McpError | IllegalStateException | 
UnsupportedOperationException ignored) {
-            return createFallbackResponse(payload, ELICITATION_FAILED_REASON, 
clientSupport);
+            return createFallbackResponse(payload, 
MCPToolElicitationFallbackReason.ELICITATION_FAILED, clientCapabilities);
         }
-        return continueOrFallback(exchange, toolDefinition, arguments, 
fallbackResponse, payload, clarificationForm.get(), continuationContext, 
elicitedResult, clientSupport);
+        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 ClientElicitationSupport 
clientSupport) {
+                                           final 
MCPClientElicitationCapabilities clientCapabilities) {
         if (null == elicitedResult || null == elicitedResult.action()) {
-            return createFallbackResponse(payload, 
MALFORMED_ELICITATION_RESULT_REASON, clientSupport);
+            return createFallbackResponse(payload, 
MCPToolElicitationFallbackReason.MALFORMED_ELICITATION_RESULT, 
clientCapabilities);
         }
         if (McpSchema.ElicitResult.Action.ACCEPT != elicitedResult.action()) {
             return fallbackResponse;
         }
         if (null == elicitedResult.content()) {
-            return createFallbackResponse(payload, 
MALFORMED_ELICITATION_RESULT_REASON, clientSupport);
+            return createFallbackResponse(payload, 
MCPToolElicitationFallbackReason.MALFORMED_ELICITATION_RESULT, 
clientCapabilities);
         }
-        if (!isActiveContinuation(exchange, toolDefinition.getDescriptor(), 
arguments, clarificationForm, continuationContext)) {
-            return createFallbackResponse(payload, STALE_ELICITATION_REASON, 
clientSupport);
+        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, 
INVALID_ELICITED_CONTENT_REASON, clientSupport);
+            return createFallbackResponse(payload, 
MCPToolElicitationFallbackReason.INVALID_ELICITED_CONTENT, clientCapabilities);
         }
         return toolController.handle(exchange.sessionId(), toolDefinition, 
clarificationPolicy.mergeArguments(arguments, clarificationForm, 
elicitedResult.content()));
     }
     
-    private ClientElicitationSupport getClientElicitationSupport(final 
McpSyncServerExchange exchange) {
-        McpSchema.ClientCapabilities clientCapabilities = 
exchange.getClientCapabilities();
-        if (null == clientCapabilities || null == 
clientCapabilities.elicitation()) {
-            return new ClientElicitationSupport(false, false);
-        }
-        McpSchema.ClientCapabilities.Elicitation elicitation = 
clientCapabilities.elicitation();
-        return new ClientElicitationSupport(null != elicitation.form() || null 
== elicitation.url(), null != elicitation.url());
+    private MCPResponse createFallbackResponse(final Map<String, Object> 
payload, final MCPToolElicitationFallbackReason fallbackReason,
+                                               final 
MCPClientElicitationCapabilities clientCapabilities) {
+        return fallbackResponseFactory.create(payload, fallbackReason, 
clientCapabilities);
     }
     
-    private String determineUnavailableFormReason(final Map<String, Object> 
payload, final ClientElicitationSupport clientSupport) {
-        if (clarificationPolicy.getPlanId(payload).trim().isEmpty()) {
-            return MISSING_PLAN_ID_REASON;
+    private MCPToolElicitationFallbackReason 
getUnavailableFormFallbackReason(final Map<String, Object> payload, final 
MCPClientElicitationCapabilities clientCapabilities) {
+        if (Objects.toString(payload.get(WorkflowFieldNames.PLAN_ID), 
"").trim().isEmpty()) {
+            return MCPToolElicitationFallbackReason.MISSING_PLAN_ID;
         }
-        if (!clarificationPolicy.hasSensitiveClarificationQuestions(payload)) {
-            return AMBIGUOUS_FIELD_BINDING_REASON;
-        }
-        return clientSupport.supportsUrlMode() ? 
URL_MODE_NOT_IMPLEMENTED_REASON : SENSITIVE_FORM_BLOCKED_REASON;
-    }
-    
-    private MCPResponse createFallbackResponse(final Map<String, Object> 
payload, final String fallbackReason, final ClientElicitationSupport 
clientSupport) {
-        Map<String, Object> result = new LinkedHashMap<>(payload);
-        if (clarificationPolicy.hasSensitiveClarificationQuestions(payload)) {
-            result.put(MCPPayloadFieldNames.CLARIFICATION_QUESTIONS, 
createSanitizedClarificationQuestions(payload));
-            result.put(MCPPayloadFieldNames.NEXT_ACTIONS, 
createSensitiveNextActions());
-        }
-        result.put(ELICITATION_SUPPORT_FIELD, 
createElicitationSupportPayload(clientSupport, 
selectFallbackInteraction(fallbackReason)));
-        result.put(FALLBACK_REASON_FIELD, fallbackReason);
-        return new MCPMapResponse(result);
-    }
-    
-    private List<Map<String, Object>> 
createSanitizedClarificationQuestions(final Map<String, Object> payload) {
-        Object clarificationQuestions = 
payload.get(MCPPayloadFieldNames.CLARIFICATION_QUESTIONS);
-        if (!(clarificationQuestions instanceof List<?> questions)) {
-            return List.of();
-        }
-        List<Map<String, Object>> result = new LinkedList<>();
-        for (Object each : questions) {
-            if (each instanceof Map<?, ?> question) {
-                result.add(createSanitizedClarificationQuestion(question));
-            }
-        }
-        return result;
-    }
-    
-    private Map<String, Object> createSanitizedClarificationQuestion(final 
Map<?, ?> question) {
-        Map<String, Object> result = new LinkedHashMap<>(4, 1F);
-        result.put(MCPPayloadFieldNames.FIELD, 
Objects.toString(question.get(MCPPayloadFieldNames.FIELD), ""));
-        result.put(MCPPayloadFieldNames.INPUT_TYPE, "secret");
-        result.put(MCPPayloadFieldNames.SECRET, true);
-        result.put(MCPPayloadFieldNames.MESSAGE, "Sensitive input must be 
provided through configured secure channels before continuing the same 
planner.");
-        return result;
-    }
-    
-    private List<Map<String, Object>> createSensitiveNextActions() {
-        Map<String, Object> result = new LinkedHashMap<>(4, 1F);
-        result.put("order", 1);
-        result.put("type", "terminal");
-        result.put("title", "Collect sensitive inputs through configured 
secure channels.");
-        result.put(MCPPayloadFieldNames.REASON, "MCP form elicitation is 
limited to non-sensitive STDIO continuations; URL mode is not implemented in 
this release.");
-        return List.of(result);
-    }
-    
-    private Map<String, Object> createElicitationSupportPayload(final 
ClientElicitationSupport clientSupport, final String selectedInteraction) {
-        Map<String, Object> result = new LinkedHashMap<>(3, 1F);
-        result.put(FORM_MODE_FIELD, clientSupport.supportsFormMode());
-        result.put(URL_MODE_FIELD, clientSupport.supportsUrlMode());
-        result.put(SELECTED_INTERACTION_FIELD, selectedInteraction);
-        return result;
-    }
-    
-    private String selectFallbackInteraction(final String fallbackReason) {
-        return URL_MODE_NOT_IMPLEMENTED_REASON.equals(fallbackReason) || 
SENSITIVE_FORM_BLOCKED_REASON.equals(fallbackReason) ? URL_FALLBACK_INTERACTION 
: STRUCTURED_FALLBACK_INTERACTION;
+        return clarificationPolicy.hasSensitiveClarificationQuestions(payload)
+                ? 
MCPToolElicitationFallbackReason.SENSITIVE_FORM_BLOCKED.withClientCapabilities(clientCapabilities)
+                : MCPToolElicitationFallbackReason.AMBIGUOUS_FIELD_BINDING;
     }
     
     private FormContinuationContext createContinuationContext(final 
McpSyncServerExchange exchange, final MCPToolDescriptor toolDescriptor, final 
Map<String, Object> arguments,
@@ -223,13 +127,6 @@ final class MCPToolElicitationHandler {
                 UUID.randomUUID().toString());
     }
     
-    private boolean isActiveContinuation(final McpSyncServerExchange exchange, 
final MCPToolDescriptor toolDescriptor, final Map<String, Object> arguments,
-                                         final 
MCPToolClarificationPolicy.ClarificationForm clarificationForm, final 
FormContinuationContext continuationContext) {
-        return STDIO_TRANSPORT.equals(activeTransport) && 
continuationContext.sessionId().equals(exchange.sessionId())
-                && 
continuationContext.toolName().equals(toolDescriptor.getName()) && 
continuationContext.planId().equals(clarificationForm.planId())
-                && continuationContext.argumentsHashCode() == 
arguments.hashCode() && 
clock.instant().isBefore(continuationContext.expiresAt());
-    }
-    
     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))
@@ -245,10 +142,13 @@ final class MCPToolElicitationHandler {
                 MCPShardingSphereMetadataKeys.FORM_REQUEST_ID, formRequestId);
     }
     
-    private record ClientElicitationSupport(boolean supportsFormMode, boolean 
supportsUrlMode) {
-        
-    }
-    
     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,
+                                 final Map<String, Object> arguments, final 
MCPToolClarificationPolicy.ClarificationForm clarificationForm) {
+            return STDIO_TRANSPORT.equals(activeTransport) && 
sessionId.equals(exchange.sessionId())
+                    && toolName.equals(toolDescriptor.getName()) && 
planId.equals(clarificationForm.planId())
+                    && argumentsHashCode == arguments.hashCode() && 
clock.instant().isBefore(expiresAt);
+        }
     }
 }
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPClientElicitationCapabilitiesTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPClientElicitationCapabilitiesTest.java
new file mode 100644
index 00000000000..81316dbe256
--- /dev/null
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPClientElicitationCapabilitiesTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.server.McpSyncServerExchange;
+import io.modelcontextprotocol.spec.McpSchema;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Collections;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class MCPClientElicitationCapabilitiesTest {
+    
+    @Test
+    void assertFromWithoutClientCapabilities() {
+        McpSyncServerExchange exchange = mock(McpSyncServerExchange.class);
+        MCPClientElicitationCapabilities actual = 
MCPClientElicitationCapabilities.from(exchange);
+        assertFalse(actual.isFormModeSupported());
+        assertFalse(actual.isUrlModeSupported());
+    }
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("getClientCapabilities")
+    void assertFrom(final String name, final McpSchema.ClientCapabilities 
clientCapabilities, final boolean expectedFormModeSupported, final boolean 
expectedUrlModeSupported) {
+        McpSyncServerExchange exchange = mock(McpSyncServerExchange.class);
+        when(exchange.getClientCapabilities()).thenReturn(clientCapabilities);
+        MCPClientElicitationCapabilities actual = 
MCPClientElicitationCapabilities.from(exchange);
+        assertFormModeSupported(actual, expectedFormModeSupported);
+        assertUrlModeSupported(actual, expectedUrlModeSupported);
+    }
+    
+    private void assertFormModeSupported(final 
MCPClientElicitationCapabilities actual, final boolean 
expectedFormModeSupported) {
+        if (expectedFormModeSupported) {
+            assertTrue(actual.isFormModeSupported());
+        } else {
+            assertFalse(actual.isFormModeSupported());
+        }
+    }
+    
+    private void assertUrlModeSupported(final MCPClientElicitationCapabilities 
actual, final boolean expectedUrlModeSupported) {
+        if (expectedUrlModeSupported) {
+            assertTrue(actual.isUrlModeSupported());
+        } else {
+            assertFalse(actual.isUrlModeSupported());
+        }
+    }
+    
+    private static Stream<Arguments> getClientCapabilities() {
+        return Stream.of(
+                Arguments.of("without elicitation capabilities", new 
McpSchema.ClientCapabilities(Collections.emptyMap(), null, null, null), false, 
false),
+                Arguments.of("with default elicitation capabilities", 
McpSchema.ClientCapabilities.builder().elicitation().build(), true, false),
+                Arguments.of("with form elicitation capabilities", new 
McpSchema.ClientCapabilities(
+                        Collections.emptyMap(), null, null,
+                        new McpSchema.ClientCapabilities.Elicitation(new 
McpSchema.ClientCapabilities.Elicitation.Form(), null)), true, false),
+                Arguments.of("with form and url elicitation capabilities", new 
McpSchema.ClientCapabilities(
+                        Collections.emptyMap(), null, null,
+                        new McpSchema.ClientCapabilities.Elicitation(new 
McpSchema.ClientCapabilities.Elicitation.Form(), new 
McpSchema.ClientCapabilities.Elicitation.Url())), true, true),
+                Arguments.of("with url elicitation capabilities", new 
McpSchema.ClientCapabilities(
+                        Collections.emptyMap(), null, null,
+                        new McpSchema.ClientCapabilities.Elicitation(null, new 
McpSchema.ClientCapabilities.Elicitation.Url())), false, true));
+    }
+}
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolClarificationPolicyTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolClarificationPolicyTest.java
index 1add55192bf..9821d95aef3 100644
--- 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolClarificationPolicyTest.java
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolClarificationPolicyTest.java
@@ -23,7 +23,10 @@ import org.junit.jupiter.api.Test;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -59,4 +62,36 @@ class MCPToolClarificationPolicyTest extends 
AbstractMCPToolSpecificationFactory
                 "status", "clarifying",
                 MCPPayloadFieldNames.CLARIFICATION_QUESTIONS, List.of())));
     }
+    
+    @Test
+    void assertCreateClarificationForm() {
+        Optional<MCPToolClarificationPolicy.ClarificationForm> actual = 
policy.createClarificationForm(createClarifyingPayload(),
+                
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+        assertTrue(actual.isPresent());
+        assertThat(actual.get().planId(), is("plan-1"));
+        assertThat(actual.get().requestedSchema(), 
is(createExpectedElicitRequestedSchema()));
+    }
+    
+    @Test
+    void assertCreateClarificationFormWithoutPlanId() {
+        Optional<MCPToolClarificationPolicy.ClarificationForm> actual = 
policy.createClarificationForm(createClarifyingPayloadWithoutPlanId(),
+                
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+        assertTrue(actual.isEmpty());
+    }
+    
+    @Test
+    void assertCreateClarificationFormWithSensitiveQuestion() {
+        Map<String, Object> payload = 
createClarifyingPayload(createClarifyingQuestion("custom_properties.access-token",
 "string", false, "Provide access token."));
+        Optional<MCPToolClarificationPolicy.ClarificationForm> actual = 
policy.createClarificationForm(payload,
+                
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+        assertTrue(actual.isEmpty());
+    }
+    
+    @Test
+    void assertCreateClarificationFormWithAmbiguousFieldBinding() {
+        Map<String, Object> payload = 
createClarifyingPayload(createClarifyingQuestion("requires_review", "boolean", 
false, "Require review?"));
+        Optional<MCPToolClarificationPolicy.ClarificationForm> actual = 
policy.createClarificationForm(payload,
+                
createAmbiguousPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+        assertTrue(actual.isEmpty());
+    }
 }
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackReasonTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackReasonTest.java
new file mode 100644
index 00000000000..4d5f3e750d1
--- /dev/null
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackReasonTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.server.McpSyncServerExchange;
+import io.modelcontextprotocol.spec.McpSchema;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Collections;
+import java.util.stream.Stream;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class MCPToolElicitationFallbackReasonTest {
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("getFallbackReasons")
+    void assertGetters(final MCPToolElicitationFallbackReason fallbackReason, 
final String expectedValue, final String expectedSelectedInteraction) {
+        assertThat(fallbackReason.getValue(), is(expectedValue));
+        assertThat(fallbackReason.getSelectedInteraction(), 
is(expectedSelectedInteraction));
+    }
+    
+    @Test
+    void assertWithClientCapabilitiesUseUrlFallback() {
+        MCPToolElicitationFallbackReason actual = 
MCPToolElicitationFallbackReason.SENSITIVE_FORM_BLOCKED.withClientCapabilities(createClientCapabilities(createFormAndUrlClientCapabilities()));
+        assertThat(actual, 
is(MCPToolElicitationFallbackReason.URL_MODE_NOT_IMPLEMENTED));
+    }
+    
+    @Test
+    void assertWithClientCapabilitiesKeepSensitiveFallback() {
+        MCPToolElicitationFallbackReason actual =
+                
MCPToolElicitationFallbackReason.SENSITIVE_FORM_BLOCKED.withClientCapabilities(createClientCapabilities(McpSchema.ClientCapabilities.builder().elicitation().build()));
+        assertThat(actual, 
is(MCPToolElicitationFallbackReason.SENSITIVE_FORM_BLOCKED));
+    }
+    
+    @Test
+    void assertWithClientCapabilitiesKeepStructuredFallback() {
+        MCPToolElicitationFallbackReason actual = 
MCPToolElicitationFallbackReason.AMBIGUOUS_FIELD_BINDING.withClientCapabilities(createClientCapabilities(createFormAndUrlClientCapabilities()));
+        assertThat(actual, 
is(MCPToolElicitationFallbackReason.AMBIGUOUS_FIELD_BINDING));
+    }
+    
+    private static Stream<Arguments> getFallbackReasons() {
+        return Stream.of(
+                
Arguments.of(MCPToolElicitationFallbackReason.CLIENT_UNSUPPORTED, 
"client_unsupported", "structured_fallback"),
+                
Arguments.of(MCPToolElicitationFallbackReason.REMOTE_IDENTITY_REQUIRED, 
"remote_identity_required", "structured_fallback"),
+                Arguments.of(MCPToolElicitationFallbackReason.MISSING_PLAN_ID, 
"missing_plan_id", "structured_fallback"),
+                
Arguments.of(MCPToolElicitationFallbackReason.SENSITIVE_FORM_BLOCKED, 
"sensitive_form_blocked", "url_fallback"),
+                
Arguments.of(MCPToolElicitationFallbackReason.URL_MODE_NOT_IMPLEMENTED, 
"url_mode_not_implemented", "url_fallback"),
+                
Arguments.of(MCPToolElicitationFallbackReason.AMBIGUOUS_FIELD_BINDING, 
"ambiguous_field_binding", "structured_fallback"),
+                
Arguments.of(MCPToolElicitationFallbackReason.ELICITATION_FAILED, 
"elicitation_failed", "structured_fallback"),
+                
Arguments.of(MCPToolElicitationFallbackReason.MALFORMED_ELICITATION_RESULT, 
"malformed_elicitation_result", "structured_fallback"),
+                
Arguments.of(MCPToolElicitationFallbackReason.INVALID_ELICITED_CONTENT, 
"invalid_elicited_content", "structured_fallback"),
+                
Arguments.of(MCPToolElicitationFallbackReason.STALE_ELICITATION, 
"stale_elicitation", "structured_fallback"));
+    }
+    
+    private MCPClientElicitationCapabilities createClientCapabilities(final 
McpSchema.ClientCapabilities clientCapabilities) {
+        McpSyncServerExchange exchange = mock(McpSyncServerExchange.class);
+        when(exchange.getClientCapabilities()).thenReturn(clientCapabilities);
+        return MCPClientElicitationCapabilities.from(exchange);
+    }
+    
+    private McpSchema.ClientCapabilities createFormAndUrlClientCapabilities() {
+        return new McpSchema.ClientCapabilities(
+                Collections.emptyMap(), null, null,
+                new McpSchema.ClientCapabilities.Elicitation(new 
McpSchema.ClientCapabilities.Elicitation.Form(), new 
McpSchema.ClientCapabilities.Elicitation.Url()));
+    }
+}
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackResponseFactoryTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackResponseFactoryTest.java
new file mode 100644
index 00000000000..17c015a79aa
--- /dev/null
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolElicitationFallbackResponseFactoryTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.server.McpSyncServerExchange;
+import io.modelcontextprotocol.spec.McpSchema;
+import org.apache.shardingsphere.mcp.api.protocol.response.MCPResponse;
+import org.apache.shardingsphere.mcp.support.protocol.MCPPayloadFieldNames;
+import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowFieldNames;
+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.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class MCPToolElicitationFallbackResponseFactoryTest extends 
AbstractMCPToolSpecificationFactoryTest {
+    
+    private final MCPToolElicitationFallbackResponseFactory factory = new 
MCPToolElicitationFallbackResponseFactory();
+    
+    @Test
+    void assertCreateStructuredFallback() {
+        MCPResponse actual = factory.create(Map.of(WorkflowFieldNames.PLAN_ID, 
"plan-1", "status", "clarifying"),
+                MCPToolElicitationFallbackReason.AMBIGUOUS_FIELD_BINDING, 
createClientCapabilities(McpSchema.ClientCapabilities.builder().elicitation().build()));
+        Map<String, Object> actualPayload = actual.toPayload();
+        assertThat(actualPayload.get("fallback_reason"), 
is("ambiguous_field_binding"));
+        Map<?, ?> actualSupport = (Map<?, ?>) 
actualPayload.get("elicitation_support");
+        assertTrue((Boolean) actualSupport.get("form_mode"));
+        assertFalse((Boolean) actualSupport.get("url_mode"));
+        assertThat(actualSupport.get("selected_interaction"), 
is("structured_fallback"));
+        
assertFalse(actualPayload.containsKey(MCPPayloadFieldNames.NEXT_ACTIONS));
+    }
+    
+    @Test
+    void assertCreateSensitiveFallback() {
+        MCPResponse actual = 
factory.create(createClarifyingPayload(createClarifyingQuestion("primary_algorithm_properties.access-token",
 "string", false, "Provide access token.")),
+                MCPToolElicitationFallbackReason.SENSITIVE_FORM_BLOCKED, 
createClientCapabilities(McpSchema.ClientCapabilities.builder().elicitation().build()));
+        Map<String, Object> actualPayload = actual.toPayload();
+        assertThat(actualPayload.get("fallback_reason"), 
is("sensitive_form_blocked"));
+        assertSensitiveFallback(actualPayload);
+    }
+    
+    private void assertSensitiveFallback(final Map<String, Object> 
actualPayload) {
+        Map<?, ?> actualQuestion = (Map<?, ?>) ((List<?>) 
actualPayload.get(MCPPayloadFieldNames.CLARIFICATION_QUESTIONS)).get(0);
+        assertThat(actualQuestion.get(MCPPayloadFieldNames.INPUT_TYPE), 
is("secret"));
+        assertTrue((boolean) actualQuestion.get(MCPPayloadFieldNames.SECRET));
+        assertThat(actualQuestion.get(MCPPayloadFieldNames.MESSAGE), 
is("Sensitive input must be provided through configured secure channels before 
continuing the same planner."));
+        
assertFalse(actualQuestion.containsKey(MCPPayloadFieldNames.DISPLAY_MESSAGE));
+        assertThat(((Map<?, ?>) ((List<?>) 
actualPayload.get(MCPPayloadFieldNames.NEXT_ACTIONS)).get(0)).get("type"), 
is("terminal"));
+        assertFalse(String.valueOf(actualPayload).contains("Provide access 
token."));
+    }
+    
+    private MCPClientElicitationCapabilities createClientCapabilities(final 
McpSchema.ClientCapabilities clientCapabilities) {
+        McpSyncServerExchange exchange = mock(McpSyncServerExchange.class);
+        when(exchange.getClientCapabilities()).thenReturn(clientCapabilities);
+        return MCPClientElicitationCapabilities.from(exchange);
+    }
+}
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationElicitationFactoryTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationElicitationFactoryTest.java
deleted file mode 100644
index 92dd1e233cf..00000000000
--- 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationElicitationFactoryTest.java
+++ /dev/null
@@ -1,397 +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.server.McpServerFeatures.SyncToolSpecification;
-import io.modelcontextprotocol.server.McpSyncServerExchange;
-import io.modelcontextprotocol.spec.McpSchema;
-import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
-import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
-import org.apache.shardingsphere.mcp.api.protocol.response.MCPResponse;
-import org.apache.shardingsphere.mcp.core.context.MCPRequestScope;
-import org.apache.shardingsphere.mcp.core.context.MCPRuntimeContext;
-import org.apache.shardingsphere.mcp.core.tool.handler.MCPToolDefinition;
-import org.apache.shardingsphere.mcp.core.tool.handler.ToolDefinitionRegistry;
-import 
org.apache.shardingsphere.mcp.support.descriptor.MCPShardingSphereMetadataKeys;
-import org.apache.shardingsphere.mcp.support.protocol.response.MCPMapResponse;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.MockedStatic;
-
-import java.time.Clock;
-import java.time.Duration;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.isA;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-class MCPToolSpecificationElicitationFactoryTest extends 
AbstractMCPToolSpecificationFactoryTest {
-    
-    @Test
-    void assertCreateToolSpecificationsHandleInteractiveElicitation() {
-        
assertInteractiveElicitation(McpSchema.ClientCapabilities.builder().elicitation().build());
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsHandleInteractiveElicitationWithFormOnlyClient() {
-        assertInteractiveElicitation(createFormOnlyClientCapabilities());
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsHandleInteractiveElicitationWithFormAndUrlClient()
 {
-        assertInteractiveElicitation(createFormAndUrlClientCapabilities());
-    }
-    
-    private void assertInteractiveElicitation(final 
McpSchema.ClientCapabilities clientCapabilities) {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            String toolName = "database_gateway_plan_encrypt_rule";
-            MCPResponse clarifyingResponse = new 
MCPMapResponse(createClarifyingPayload());
-            MCPResponse plannedResponse = new MCPMapResponse(Map.of("status", 
"planned"));
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
-            mockedToolDefinitionRegistry.when(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), any()))
-                    .thenReturn(clarifyingResponse,
-                            plannedResponse);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT,
-                    Map.of("field_1", "foo_display", "field_2", true)), 
clientCapabilities);
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
-            assertThat(actual.structuredContent(), is(Map.of("status", 
"planned")));
-            ArgumentCaptor<McpSchema.ElicitRequest> requestCaptor = 
ArgumentCaptor.forClass(McpSchema.ElicitRequest.class);
-            verify(exchange).createElicitation(requestCaptor.capture());
-            
assertThat(requestCaptor.getValue().meta().get(MCPShardingSphereMetadataKeys.TOOL),
 is(toolName));
-            
assertThat(requestCaptor.getValue().meta().get(MCPShardingSphereMetadataKeys.PLAN_ID),
 is("plan-1"));
-            
assertThat(requestCaptor.getValue().meta().get(MCPShardingSphereMetadataKeys.FORM_REQUEST_ID),
 isA(String.class));
-            assertThat(requestCaptor.getValue().requestedSchema(), 
is(createExpectedElicitRequestedSchema()));
-            mockedToolDefinitionRegistry.verify(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), eq(createElicitedArguments())));
-        }
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsSkipElicitationWithSecretQuestion() {
-        
assertCreateToolSpecificationsSkipUnsafeElicitation(createClarifyingQuestion("custom_properties.display-name",
 "string", true, "Provide display name."));
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsSkipElicitationWithSecretInputType() {
-        
assertCreateToolSpecificationsSkipUnsafeElicitation(createClarifyingQuestion("custom_properties.display-name",
 "secret", false, "Provide display name."));
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsSkipElicitationWithSensitiveFieldName() 
{
-        
assertCreateToolSpecificationsSkipUnsafeElicitation(createClarifyingQuestion("primary_algorithm_properties.access-token",
 "string", false, "Provide access token."));
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsFallbackWithUrlModeForSensitiveQuestion() {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            String toolName = "database_gateway_plan_encrypt_rule";
-            MCPResponse response = new MCPMapResponse(createClarifyingPayload(
-                    
createClarifyingQuestion("primary_algorithm_properties.access-token", "string", 
false, "Provide access token.")));
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of()), 
createFormAndUrlClientCapabilities());
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
-            assertStructuredFallback(actual, "url_mode_not_implemented", true, 
true, "url_fallback");
-            assertSanitizedSensitiveFallback(actual);
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsSkipElicitationWithoutPlanId() {
-        
assertCreateToolSpecificationsSkipUnsafeElicitationWithPayload(createClarifyingPayloadWithoutPlanId(),
 "missing_plan_id", "structured_fallback");
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsSkipElicitationWithUnknownAlgorithmSecretFlag() {
-        assertCreateToolSpecificationsSkipUnsafeElicitationWithPayload(Map.of(
-                "plan_id", "plan-1",
-                "status", "clarifying",
-                "clarification_questions", List.of(Map.of(
-                        "field", "primary_algorithm_properties.props",
-                        "input_type", "string",
-                        "display_message", "Provide props."))),
-                "sensitive_form_blocked", "url_fallback");
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsSkipElicitationWithAmbiguousFieldBinding() {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            String toolName = "database_gateway_plan_encrypt_rule";
-            Map<String, Object> expectedPayload = 
createClarifyingPayload(createClarifyingQuestion("requires_review", "boolean", 
false, "Require review?"));
-            MCPResponse response = new MCPMapResponse(expectedPayload);
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createAmbiguousPlanningToolDescriptor(toolName));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
true)));
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
-            assertStructuredFallback(actual, "ambiguous_field_binding", true, 
false, "structured_fallback");
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsSkipElicitationForUrlOnlyClient() {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            String toolName = "database_gateway_plan_encrypt_rule";
-            Map<String, Object> expectedPayload = createClarifyingPayload();
-            MCPResponse response = new MCPMapResponse(expectedPayload);
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = 
createUrlOnlyElicitationExchange();
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
-            assertStructuredFallback(actual, "client_unsupported", false, 
true, "structured_fallback");
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsFallbackForStreamableHttp() {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            String toolName = "database_gateway_plan_encrypt_rule";
-            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("http")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
"foo_display")));
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
-            assertStructuredFallback(actual, "remote_identity_required", true, 
false, "structured_fallback");
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    private void assertCreateToolSpecificationsSkipUnsafeElicitation(final 
Map<String, Object> question) {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            String toolName = "database_gateway_plan_encrypt_rule";
-            Map<String, Object> expectedPayload = 
createClarifyingPayload(question);
-            MCPResponse response = new MCPMapResponse(expectedPayload);
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, 
Map.of("custom_properties.display-name", "foo_display")));
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
-            assertStructuredFallback(actual, "sensitive_form_blocked", true, 
false, "url_fallback");
-            assertSanitizedSensitiveFallback(actual);
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    private void 
assertCreateToolSpecificationsSkipUnsafeElicitationWithPayload(final 
Map<String, Object> expectedPayload, final String expectedReason,
-                                                                               
 final String expectedInteraction) {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            String toolName = "database_gateway_plan_encrypt_rule";
-            MCPResponse response = new MCPMapResponse(expectedPayload);
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
"foo_display")));
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
-            assertStructuredFallback(actual, expectedReason, true, false, 
expectedInteraction);
-            if ("url_fallback".equals(expectedInteraction)) {
-                assertSanitizedSensitiveFallback(actual);
-            }
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsSkipElicitationForNonPlanningTool() {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            Map<String, Object> expectedPayload = createClarifyingPayload();
-            MCPResponse response = new MCPMapResponse(expectedPayload);
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createToolDescriptor("database_gateway_search_metadata"));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            MCPRuntimeContext runtimeContext = mock(MCPRuntimeContext.class, 
RETURNS_DEEP_STUBS);
-            
when(runtimeContext.getSessionManager().getTransactionResourceManager().getRuntimeDatabases()).thenReturn(Collections.emptyMap());
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(runtimeContext).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of()));
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_search_metadata", Map.of()));
-            assertThat(actual.structuredContent(), is(expectedPayload));
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsSkipElicitationWithoutRuntimeDescriptor() {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            Map<String, Object> expectedPayload = createClarifyingPayload();
-            MCPResponse response = new MCPMapResponse(expectedPayload);
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createToolDescriptor("fixture_ping"));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            MCPRuntimeContext runtimeContext = mock(MCPRuntimeContext.class, 
RETURNS_DEEP_STUBS);
-            
when(runtimeContext.getSessionManager().getTransactionResourceManager().getRuntimeDatabases()).thenReturn(Collections.emptyMap());
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(runtimeContext).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of()));
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("fixture_ping", Map.of()));
-            assertThat(actual.structuredContent(), is(expectedPayload));
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsFallbackWithoutElicitation() {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = mock(McpSyncServerExchange.class);
-            when(exchange.sessionId()).thenReturn("session-id");
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
-            assertStructuredFallback(actual, "client_unsupported", false, 
false, "structured_fallback");
-            verify(exchange, never()).createElicitation(any());
-        }
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsFallbackWhenElicitationDeclined() {
-        
assertCreateToolSpecificationsFallbackWhenElicitationAction(McpSchema.ElicitResult.Action.DECLINE);
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsFallbackWhenElicitationCancelled() {
-        
assertCreateToolSpecificationsFallbackWhenElicitationAction(McpSchema.ElicitResult.Action.CANCEL);
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentHasExtraField() {
-        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(Map.of("field_1",
 "foo_display", "field_2", true, "field_3", "unexpected"));
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentMissesField() {
-        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(Map.of("field_1",
 "foo_display"));
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentHasBlankRequiredValue()
 {
-        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(Map.of("field_1",
 " ", "field_2", true));
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentTypeMismatches() {
-        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(Map.of("field_1",
 1, "field_2", true));
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentViolatesAllowedValues()
 {
-        Map<String, Object> expectedPayload = 
createClarifyingPayload(createClarifyingQuestion(
-                "custom_properties.display-name", "string", false, "Provide 
display name.", List.of("foo_display")));
-        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(expectedPayload,
 Map.of("field_1", "bar_display"));
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsFallbackWhenElicitationFails() {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = 
createThrowingElicitationExchange();
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
-            assertStructuredFallback(actual, "elicitation_failed", true, 
false, "structured_fallback");
-            verify(exchange).createElicitation(any());
-        }
-    }
-    
-    @Test
-    void 
assertCreateToolSpecificationsFallbackWhenElicitationResultMalformed() {
-        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid("malformed_elicitation_result",
 new McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, null));
-    }
-    
-    @Test
-    void assertCreateToolSpecificationsFallbackWhenElicitationExpires() {
-        try (
-                MockedStatic<ToolDefinitionRegistry> 
mockedToolDefinitionRegistry = mockStatic(ToolDefinitionRegistry.class);
-                MockedStatic<Clock> mockedClock = mockStatic(Clock.class)) {
-            MutableClock clock = new MutableClock();
-            mockedClock.when(Clock::systemUTC).thenReturn(clock);
-            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
"foo_display", "field_2", true)));
-            when(exchange.createElicitation(any())).thenAnswer(invocation -> {
-                clock.advance(Duration.ofMinutes(11L));
-                return new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
"foo_display", "field_2", true));
-            });
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
-            assertStructuredFallback(actual, "stale_elicitation", true, false, 
"structured_fallback");
-            mockedToolDefinitionRegistry.verify(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), eq(createElicitedArguments())), never());
-        }
-    }
-    
-    private void 
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(final 
Map<String, Object> elicitedContent) {
-        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(createClarifyingPayload(),
 elicitedContent);
-    }
-    
-    private void 
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(final 
Map<String, Object> expectedPayload, final Map<String, Object> elicitedContent) 
{
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            MCPResponse response = new MCPMapResponse(expectedPayload);
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, elicitedContent));
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
-            assertStructuredFallback(actual, "invalid_elicited_content", true, 
false, "structured_fallback");
-            verify(exchange).createElicitation(any());
-            mockedToolDefinitionRegistry.verify(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), eq(createElicitedArguments())), never());
-        }
-    }
-    
-    private void 
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(final String 
expectedReason, final McpSchema.ElicitResult elicitedResult) {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = 
createElicitationExchange(elicitedResult);
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
-            assertStructuredFallback(actual, expectedReason, true, false, 
"structured_fallback");
-            verify(exchange).createElicitation(any());
-            mockedToolDefinitionRegistry.verify(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), eq(createElicitedArguments())), never());
-        }
-    }
-    
-    private void 
assertCreateToolSpecificationsFallbackWhenElicitationAction(final 
McpSchema.ElicitResult.Action action) {
-        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
-            Map<String, Object> expectedPayload = createClarifyingPayload();
-            MCPResponse response = new MCPMapResponse(expectedPayload);
-            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
-            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
-            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
-            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(action, Map.of()));
-            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
-            assertThat(actual.structuredContent(), is(expectedPayload));
-            verify(exchange).createElicitation(any());
-        }
-    }
-}
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationFactoryTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationFactoryTest.java
index fd59a5049c3..3e75d1e7d9d 100644
--- 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationFactoryTest.java
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPToolSpecificationFactoryTest.java
@@ -31,6 +31,7 @@ import io.modelcontextprotocol.spec.McpSchema.TextContent;
 import org.apache.shardingsphere.mcp.api.protocol.response.MCPResponse;
 import org.apache.shardingsphere.mcp.api.tool.descriptor.MCPToolAnnotations;
 import org.apache.shardingsphere.mcp.api.tool.descriptor.MCPToolDescriptor;
+import org.apache.shardingsphere.mcp.core.context.MCPRequestScope;
 import org.apache.shardingsphere.mcp.core.context.MCPRuntimeContext;
 import 
org.apache.shardingsphere.mcp.core.protocol.exception.UnsupportedToolException;
 import org.apache.shardingsphere.mcp.core.protocol.response.MCPErrorResponse;
@@ -40,8 +41,11 @@ import 
org.apache.shardingsphere.mcp.support.descriptor.MCPShardingSphereMetadat
 import org.apache.shardingsphere.mcp.support.protocol.MCPResourceHintUtils;
 import org.apache.shardingsphere.mcp.support.protocol.response.MCPMapResponse;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.MockedStatic;
 
+import java.time.Clock;
+import java.time.Duration;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -54,9 +58,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 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.eq;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 class MCPToolSpecificationFactoryTest extends 
AbstractMCPToolSpecificationFactoryTest {
@@ -282,6 +290,362 @@ class MCPToolSpecificationFactoryTest extends 
AbstractMCPToolSpecificationFactor
         }
     }
     
+    @Test
+    void assertCreateToolSpecificationsHandleInteractiveElicitation() {
+        
assertInteractiveElicitation(McpSchema.ClientCapabilities.builder().elicitation().build());
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsHandleInteractiveElicitationWithFormOnlyClient() {
+        assertInteractiveElicitation(createFormOnlyClientCapabilities());
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsHandleInteractiveElicitationWithFormAndUrlClient()
 {
+        assertInteractiveElicitation(createFormAndUrlClientCapabilities());
+    }
+    
+    private void assertInteractiveElicitation(final 
McpSchema.ClientCapabilities clientCapabilities) {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            String toolName = "database_gateway_plan_encrypt_rule";
+            MCPResponse clarifyingResponse = new 
MCPMapResponse(createClarifyingPayload());
+            MCPResponse plannedResponse = new MCPMapResponse(Map.of("status", 
"planned"));
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
+            mockedToolDefinitionRegistry.when(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), any()))
+                    .thenReturn(clarifyingResponse, plannedResponse);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT,
+                    Map.of("field_1", "foo_display", "field_2", true)), 
clientCapabilities);
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
+            assertThat(actual.structuredContent(), is(Map.of("status", 
"planned")));
+            ArgumentCaptor<McpSchema.ElicitRequest> requestCaptor = 
ArgumentCaptor.forClass(McpSchema.ElicitRequest.class);
+            verify(exchange).createElicitation(requestCaptor.capture());
+            
assertThat(requestCaptor.getValue().meta().get(MCPShardingSphereMetadataKeys.TOOL),
 is(toolName));
+            
assertThat(requestCaptor.getValue().meta().get(MCPShardingSphereMetadataKeys.PLAN_ID),
 is("plan-1"));
+            
assertThat(requestCaptor.getValue().meta().get(MCPShardingSphereMetadataKeys.FORM_REQUEST_ID),
 isA(String.class));
+            assertThat(requestCaptor.getValue().requestedSchema(), 
is(createExpectedElicitRequestedSchema()));
+            mockedToolDefinitionRegistry.verify(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), eq(createElicitedArguments())));
+        }
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsSkipElicitationWithSecretQuestion() {
+        
assertCreateToolSpecificationsSkipUnsafeElicitation(createClarifyingQuestion("custom_properties.display-name",
 "string", true, "Provide display name."));
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsSkipElicitationWithSecretInputType() {
+        
assertCreateToolSpecificationsSkipUnsafeElicitation(createClarifyingQuestion("custom_properties.display-name",
 "secret", false, "Provide display name."));
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsSkipElicitationWithSensitiveFieldName() 
{
+        
assertCreateToolSpecificationsSkipUnsafeElicitation(createClarifyingQuestion("primary_algorithm_properties.access-token",
 "string", false, "Provide access token."));
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsFallbackWithUrlModeForSensitiveQuestion() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            String toolName = "database_gateway_plan_encrypt_rule";
+            MCPResponse response = new MCPMapResponse(createClarifyingPayload(
+                    
createClarifyingQuestion("primary_algorithm_properties.access-token", "string", 
false, "Provide access token.")));
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of()), 
createFormAndUrlClientCapabilities());
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
+            assertStructuredFallback(actual, "url_mode_not_implemented", true, 
true, "url_fallback");
+            assertSanitizedSensitiveFallback(actual);
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsSkipElicitationWithoutPlanId() {
+        
assertCreateToolSpecificationsSkipUnsafeElicitationWithPayload(createClarifyingPayloadWithoutPlanId(),
 "missing_plan_id", "structured_fallback");
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsSkipElicitationWithUnknownAlgorithmSecretFlag() {
+        assertCreateToolSpecificationsSkipUnsafeElicitationWithPayload(Map.of(
+                "plan_id", "plan-1",
+                "status", "clarifying",
+                "clarification_questions", List.of(Map.of(
+                        "field", "primary_algorithm_properties.props",
+                        "input_type", "string",
+                        "display_message", "Provide props."))),
+                "sensitive_form_blocked", "url_fallback");
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsSkipElicitationWithAmbiguousFieldBinding() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            String toolName = "database_gateway_plan_encrypt_rule";
+            Map<String, Object> expectedPayload = 
createClarifyingPayload(createClarifyingQuestion("requires_review", "boolean", 
false, "Require review?"));
+            MCPResponse response = new MCPMapResponse(expectedPayload);
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createAmbiguousPlanningToolDescriptor(toolName));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
true)));
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
+            assertStructuredFallback(actual, "ambiguous_field_binding", true, 
false, "structured_fallback");
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsSkipElicitationForUrlOnlyClient() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            String toolName = "database_gateway_plan_encrypt_rule";
+            Map<String, Object> expectedPayload = createClarifyingPayload();
+            MCPResponse response = new MCPMapResponse(expectedPayload);
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = 
createUrlOnlyElicitationExchange();
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
+            assertStructuredFallback(actual, "client_unsupported", false, 
true, "structured_fallback");
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsFallbackForStreamableHttp() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            String toolName = "database_gateway_plan_encrypt_rule";
+            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("http")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
"foo_display")));
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
+            assertStructuredFallback(actual, "remote_identity_required", true, 
false, "structured_fallback");
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    private void assertCreateToolSpecificationsSkipUnsafeElicitation(final 
Map<String, Object> question) {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            String toolName = "database_gateway_plan_encrypt_rule";
+            Map<String, Object> expectedPayload = 
createClarifyingPayload(question);
+            MCPResponse response = new MCPMapResponse(expectedPayload);
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, 
Map.of("custom_properties.display-name", "foo_display")));
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
+            assertStructuredFallback(actual, "sensitive_form_blocked", true, 
false, "url_fallback");
+            assertSanitizedSensitiveFallback(actual);
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    private void 
assertCreateToolSpecificationsSkipUnsafeElicitationWithPayload(final 
Map<String, Object> expectedPayload, final String expectedReason,
+                                                                               
 final String expectedInteraction) {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            String toolName = "database_gateway_plan_encrypt_rule";
+            MCPResponse response = new MCPMapResponse(expectedPayload);
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor(toolName));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
"foo_display")));
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new CallToolRequest(toolName, 
Map.of()));
+            assertStructuredFallback(actual, expectedReason, true, false, 
expectedInteraction);
+            if ("url_fallback".equals(expectedInteraction)) {
+                assertSanitizedSensitiveFallback(actual);
+            }
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsSkipElicitationForNonPlanningTool() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            Map<String, Object> expectedPayload = createClarifyingPayload();
+            MCPResponse response = new MCPMapResponse(expectedPayload);
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createToolDescriptor("database_gateway_search_metadata"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            MCPRuntimeContext runtimeContext = mock(MCPRuntimeContext.class, 
RETURNS_DEEP_STUBS);
+            
when(runtimeContext.getSessionManager().getTransactionResourceManager().getRuntimeDatabases()).thenReturn(Collections.emptyMap());
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(runtimeContext).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of()));
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_search_metadata", Map.of()));
+            assertThat(actual.structuredContent(), is(expectedPayload));
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsSkipElicitationWithoutRuntimeDescriptor() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            Map<String, Object> expectedPayload = createClarifyingPayload();
+            MCPResponse response = new MCPMapResponse(expectedPayload);
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createToolDescriptor("fixture_ping"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            MCPRuntimeContext runtimeContext = mock(MCPRuntimeContext.class, 
RETURNS_DEEP_STUBS);
+            
when(runtimeContext.getSessionManager().getTransactionResourceManager().getRuntimeDatabases()).thenReturn(Collections.emptyMap());
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(runtimeContext).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of()));
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("fixture_ping", Map.of()));
+            assertThat(actual.structuredContent(), is(expectedPayload));
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsFallbackWithoutElicitation() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = mock(McpSyncServerExchange.class);
+            when(exchange.sessionId()).thenReturn("session-id");
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
+            assertStructuredFallback(actual, "client_unsupported", false, 
false, "structured_fallback");
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsFallbackWithoutElicitationCapabilities() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSchema.ClientCapabilities clientCapabilities = new 
McpSchema.ClientCapabilities(Collections.emptyMap(), null, null, null);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of()), 
clientCapabilities);
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
+            assertStructuredFallback(actual, "client_unsupported", false, 
false, "structured_fallback");
+            verify(exchange, never()).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsFallbackWhenElicitationDeclined() {
+        
assertCreateToolSpecificationsFallbackWhenElicitationAction(McpSchema.ElicitResult.Action.DECLINE);
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsFallbackWhenElicitationCancelled() {
+        
assertCreateToolSpecificationsFallbackWhenElicitationAction(McpSchema.ElicitResult.Action.CANCEL);
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentHasExtraField() {
+        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(Map.of("field_1",
 "foo_display", "field_2", true, "field_3", "unexpected"));
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentMissesField() {
+        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(Map.of("field_1",
 "foo_display"));
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentHasBlankRequiredValue()
 {
+        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(Map.of("field_1",
 " ", "field_2", true));
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentTypeMismatches() {
+        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(Map.of("field_1",
 1, "field_2", true));
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsFallbackWhenElicitedContentViolatesAllowedValues()
 {
+        Map<String, Object> expectedPayload = 
createClarifyingPayload(createClarifyingQuestion(
+                "custom_properties.display-name", "string", false, "Provide 
display name.", List.of("foo_display")));
+        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(expectedPayload,
 Map.of("field_1", "bar_display"));
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsFallbackWhenElicitationFails() {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = 
createThrowingElicitationExchange();
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
+            assertStructuredFallback(actual, "elicitation_failed", true, 
false, "structured_fallback");
+            verify(exchange).createElicitation(any());
+        }
+    }
+    
+    @Test
+    void 
assertCreateToolSpecificationsFallbackWhenElicitationResultMalformed() {
+        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid("malformed_elicitation_result",
 new McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, null));
+    }
+    
+    @Test
+    void assertCreateToolSpecificationsFallbackWhenElicitationExpires() {
+        try (
+                MockedStatic<ToolDefinitionRegistry> 
mockedToolDefinitionRegistry = mockStatic(ToolDefinitionRegistry.class);
+                MockedStatic<Clock> mockedClock = mockStatic(Clock.class)) {
+            MutableClock clock = new MutableClock();
+            mockedClock.when(Clock::systemUTC).thenReturn(clock);
+            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
"foo_display", "field_2", true)));
+            when(exchange.createElicitation(any())).thenAnswer(invocation -> {
+                clock.advance(Duration.ofMinutes(11L));
+                return new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of("field_1", 
"foo_display", "field_2", true));
+            });
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
+            assertStructuredFallback(actual, "stale_elicitation", true, false, 
"structured_fallback");
+            mockedToolDefinitionRegistry.verify(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), eq(createElicitedArguments())), never());
+        }
+    }
+    
+    private void 
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(final 
Map<String, Object> elicitedContent) {
+        
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(createClarifyingPayload(),
 elicitedContent);
+    }
+    
+    private void 
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(final 
Map<String, Object> expectedPayload, final Map<String, Object> elicitedContent) 
{
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            MCPResponse response = new MCPMapResponse(expectedPayload);
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, elicitedContent));
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
+            assertStructuredFallback(actual, "invalid_elicited_content", true, 
false, "structured_fallback");
+            verify(exchange).createElicitation(any());
+            mockedToolDefinitionRegistry.verify(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), eq(createElicitedArguments())), never());
+        }
+    }
+    
+    private void 
assertCreateToolSpecificationsFallbackWhenElicitedContentInvalid(final String 
expectedReason, final McpSchema.ElicitResult elicitedResult) {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            MCPResponse response = new 
MCPMapResponse(createClarifyingPayload());
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = 
createElicitationExchange(elicitedResult);
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
+            assertStructuredFallback(actual, expectedReason, true, false, 
"structured_fallback");
+            verify(exchange).createElicitation(any());
+            mockedToolDefinitionRegistry.verify(() -> 
ToolDefinitionRegistry.dispatch(any(MCPRequestScope.class), eq(toolDefinition), 
eq("session-id"), eq(createElicitedArguments())), never());
+        }
+    }
+    
+    private void 
assertCreateToolSpecificationsFallbackWhenElicitationAction(final 
McpSchema.ElicitResult.Action action) {
+        try (MockedStatic<ToolDefinitionRegistry> mockedToolDefinitionRegistry 
= mockStatic(ToolDefinitionRegistry.class)) {
+            Map<String, Object> expectedPayload = createClarifyingPayload();
+            MCPResponse response = new MCPMapResponse(expectedPayload);
+            MCPToolDefinition toolDefinition = 
mockSupportedTool(mockedToolDefinitionRegistry, 
createPlanningToolDescriptor("database_gateway_plan_encrypt_rule"));
+            mockToolDispatch(mockedToolDefinitionRegistry, toolDefinition, 
Map.of(), response);
+            SyncToolSpecification actualSpecification = new 
MCPToolSpecificationFactory(createRuntimeContext("stdio")).createToolSpecifications().get(0);
+            McpSyncServerExchange exchange = createElicitationExchange(new 
McpSchema.ElicitResult(action, Map.of()));
+            CallToolResult actual = 
actualSpecification.callHandler().apply(exchange, new 
CallToolRequest("database_gateway_plan_encrypt_rule", Map.of()));
+            assertThat(actual.structuredContent(), is(expectedPayload));
+            verify(exchange).createElicitation(any());
+        }
+    }
+    
     @Test
     void assertToolOutputSchemaExamplesMatchSchemas() {
         JsonSchemaValidator validator = new DefaultJsonSchemaValidator();

Reply via email to