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 11286753580 Enhance MCP model-facing guidance contract (#39015)
11286753580 is described below

commit 112867535801ca378c308ed3ae768159b2c7ad3b
Author: Liang Zhang <[email protected]>
AuthorDate: Mon Jul 6 00:18:57 2026 +0800

    Enhance MCP model-facing guidance contract (#39015)
    
    * Support metadata URL fallback for SQLException
    
    * Enhance MCP model-facing guidance contract
    
    - Add concise summaries, first-call routes, and transport security guidance
    - Expose typed self_resource navigation while preserving self_uri 
compatibility
    - Promote recovery next_actions to the top level for easier model execution
    - Sync MCP descriptors, LLM formatter coverage, and guidance baseline
---
 .../tool/MCPResourceLinkCandidateCollector.java    | 10 ++--
 .../tool/MCPCallToolResultFactoryTest.java         | 10 ++++
 .../core/protocol/response/MCPErrorResponse.java   |  6 ++-
 .../handler/capability/RuntimeStatusHandler.java   | 23 +++++++--
 .../handler/metadata/MetadataResourceHandler.java  | 26 ++++++++--
 .../handler/execute/ExecuteUpdateToolHandler.java  |  6 ++-
 .../metadata/SearchMetadataToolHandler.java        |  7 ++-
 .../mcp-descriptors/mcp-descriptor-core.yaml       | 46 ++++++++++++++++++
 .../protocol/response/MCPErrorResponseTest.java    |  9 ++++
 .../capability/RuntimeStatusHandlerTest.java       |  6 ++-
 .../capability/ServerCapabilitiesHandlerTest.java  |  8 ++++
 .../metadata/MetadataResourceHandlerTest.java      | 55 +++++++++++++++-------
 .../execute/ExecuteUpdateToolHandlerTest.java      |  1 +
 .../metadata/SearchMetadataToolHandlerTest.java    |  4 ++
 .../response/RuntimeDatabaseValidationResult.java  | 12 ++++-
 .../tool/response/SQLExecutionResponse.java        | 16 ++++++-
 .../descriptor/MCPGuidancePayloadBuilder.java      | 26 +++++++++-
 .../protocol/MCPModelFacingPayloadContract.java    |  6 +--
 .../mcp/support/protocol/MCPPayloadFieldNames.java |  4 ++
 .../RuntimeDatabaseValidationResultTest.java       | 10 ++++
 .../tool/response/SQLExecutionResponseTest.java    | 11 +++--
 .../descriptor/MCPGuidancePayloadBuilderTest.java  |  9 ++++
 .../MCPModelFacingPayloadContractTest.java         |  2 +
 ...LLMMCPModelFacingToolResponseFormatterTest.java | 46 +++++++++---------
 .../baseline-contract/model-contract/guidance.yaml | 17 +++++++
 25 files changed, 314 insertions(+), 62 deletions(-)

diff --git 
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkCandidateCollector.java
 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkCandidateCollector.java
index e853568b067..2fede68eb3e 100644
--- 
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkCandidateCollector.java
+++ 
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPResourceLinkCandidateCollector.java
@@ -50,6 +50,7 @@ final class MCPResourceLinkCandidateCollector {
     
     private void collectResourceLinkFields(final Map<?, ?> value, final 
List<OrderedResourceLinkCandidate> candidates) {
         
collectResourceLinkValue(value.get(MCPPayloadFieldNames.RESOURCES_TO_READ), 
MCPPayloadFieldNames.RESOURCES_TO_READ, candidates);
+        
collectResourceLinkValue(value.get(MCPPayloadFieldNames.SELF_RESOURCE), 
MCPPayloadFieldNames.SELF_RESOURCE, candidates);
         collectResourceLinkValue(value.get(MCPPayloadFieldNames.RESOURCE), 
MCPPayloadFieldNames.RESOURCE, candidates);
         
collectResourceLinkValue(value.get(MCPPayloadFieldNames.PARENT_RESOURCE), 
MCPPayloadFieldNames.PARENT_RESOURCE, candidates);
         
collectResourceLinkValue(value.get(MCPPayloadFieldNames.NEXT_RESOURCES), 
MCPPayloadFieldNames.NEXT_RESOURCES, candidates);
@@ -125,13 +126,16 @@ final class MCPResourceLinkCandidateCollector {
         if (MCPPayloadFieldNames.RESOURCES_TO_READ.equals(sourceField)) {
             return 0;
         }
-        if (MCPPayloadFieldNames.RESOURCE.equals(sourceField)) {
+        if (MCPPayloadFieldNames.SELF_RESOURCE.equals(sourceField)) {
             return 1;
         }
-        if (MCPPayloadFieldNames.PARENT_RESOURCE.equals(sourceField)) {
+        if (MCPPayloadFieldNames.RESOURCE.equals(sourceField)) {
             return 2;
         }
-        return MCPPayloadFieldNames.NEXT_RESOURCES.equals(sourceField) ? 3 : 4;
+        if (MCPPayloadFieldNames.PARENT_RESOURCE.equals(sourceField)) {
+            return 3;
+        }
+        return MCPPayloadFieldNames.NEXT_RESOURCES.equals(sourceField) ? 4 : 5;
     }
     
     private Map<String, OrderedResourceLinkCandidate> 
deduplicateCandidates(final List<OrderedResourceLinkCandidate> candidates) {
diff --git 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPCallToolResultFactoryTest.java
 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPCallToolResultFactoryTest.java
index 80a5d9a28ac..9841268eec8 100644
--- 
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPCallToolResultFactoryTest.java
+++ 
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/tool/MCPCallToolResultFactoryTest.java
@@ -135,6 +135,16 @@ class MCPCallToolResultFactoryTest extends 
AbstractMCPToolSpecificationFactoryTe
         assertThat(actualLink.mimeType(), is("application/json"));
     }
     
+    @Test
+    void assertCreateToolSpecificationsHandleSelfResourceLink() {
+        Map<String, Object> payload = Map.of("self_resource",
+                
MCPResourceHintUtils.create("shardingsphere://databases/logic_db", 
"logical-database", "inspect_self", "Read logical database.", "self_resource"));
+        CallToolResult actual = createCallToolResult("fixture_ping", new 
MCPMapResponse(payload));
+        assertThat(actual.content().get(1), isA(ResourceLink.class));
+        assertThat(((ResourceLink) actual.content().get(1)).uri(), 
is("shardingsphere://databases/logic_db"));
+        assertThat(((Map<?, ?>) 
actual.content().get(1).meta()).get(MCPShardingSphereMetadataKeys.SOURCE_FIELD),
 is("self_resource"));
+    }
+    
     @Test
     void assertCreateToolSpecificationsHandleItemResourceLinks() {
         Map<String, Object> payload = Map.of("items", List.of(Map.of(
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/protocol/response/MCPErrorResponse.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/protocol/response/MCPErrorResponse.java
index fef201617ef..114f1ac18d6 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/protocol/response/MCPErrorResponse.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/protocol/response/MCPErrorResponse.java
@@ -50,12 +50,16 @@ public final class MCPErrorResponse implements MCPResponse {
     
     @Override
     public Map<String, Object> toPayload() {
-        Map<String, Object> result = new LinkedHashMap<>(4, 1F);
+        Map<String, Object> result = new LinkedHashMap<>(6, 1F);
         result.put("response_mode", MCPResponseMode.RECOVERY);
+        result.put(MCPPayloadFieldNames.SUMMARY, message.isEmpty() ? "Recovery 
guidance is available." : message);
         result.put("request_id", requestId);
         result.put(MCPPayloadFieldNames.MESSAGE, message);
         if (!recovery.isEmpty()) {
             result.put(MCPPayloadFieldNames.RECOVERY, createRecoveryPayload());
+            if (recovery.containsKey(MCPPayloadFieldNames.NEXT_ACTIONS)) {
+                result.put(MCPPayloadFieldNames.NEXT_ACTIONS, 
recovery.get(MCPPayloadFieldNames.NEXT_ACTIONS));
+            }
         }
         return result;
     }
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/RuntimeStatusHandler.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/RuntimeStatusHandler.java
index 6abfca627c9..9ba72864b20 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/RuntimeStatusHandler.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/RuntimeStatusHandler.java
@@ -58,12 +58,14 @@ public final class RuntimeStatusHandler implements 
MCPResourceHandler<MCPDatabas
     public MCPResponse handle(final MCPDatabaseHandlerContext handlerContext, 
final MCPUriVariables uriVariables) {
         List<MCPDatabaseMetadata> databases = 
handlerContext.getMetadataQueryFacade().queryDatabases();
         boolean hasConfiguredDatabase = !databases.isEmpty();
-        Map<String, Object> result = new LinkedHashMap<>(13, 1F);
+        Map<String, Object> result = new LinkedHashMap<>(15, 1F);
         result.put("response_mode", MCPResponseMode.RUNTIME);
+        result.put(MCPPayloadFieldNames.SUMMARY, 
createSummary(hasConfiguredDatabase, databases.size()));
         result.put("server_status", hasConfiguredDatabase ? "ready" : 
"configuration_required");
         result.put("status", hasConfiguredDatabase ? "available" : 
"configuration_required");
         result.put("transport", handlerContext.getActiveTransport());
         result.put("active_transport", handlerContext.getActiveTransport());
+        result.put("transport_security_summary", 
createTransportSecuritySummary(handlerContext.getActiveTransport()));
         result.put("configured_database_count", databases.size());
         result.put("databases", databases.stream().map(each -> 
createDatabaseStatus(handlerContext, each)).toList());
         result.put("readiness", createReadiness(hasConfiguredDatabase));
@@ -75,6 +77,21 @@ public final class RuntimeStatusHandler implements 
MCPResourceHandler<MCPDatabas
         return new MCPMapResponse(result);
     }
     
+    private String createSummary(final boolean hasConfiguredDatabase, final 
int configuredDatabaseCount) {
+        return hasConfiguredDatabase
+                ? String.format("Runtime is ready with %d configured logical 
database(s).", configuredDatabaseCount)
+                : "Runtime requires at least one configured logical database 
before metadata discovery or SQL execution.";
+    }
+    
+    private Map<String, Object> createTransportSecuritySummary(final String 
activeTransport) {
+        Map<String, Object> result = new LinkedHashMap<>(4, 1F);
+        result.put("transport", activeTransport);
+        result.put("authentication", "http".equalsIgnoreCase(activeTransport) 
? "not_enabled_by_mcp_transport" : "local_client_process");
+        result.put("recommended_exposure", 
"http".equalsIgnoreCase(activeTransport) ? "loopback_or_trusted_gateway" : 
"local_stdio_session");
+        result.put("model_action", "Do not request or echo JDBC URLs, 
credentials, raw environment variables, or stack traces.");
+        return result;
+    }
+    
     private Map<String, Object> createReadiness(final boolean 
hasConfiguredDatabase) {
         Map<String, Object> result = new LinkedHashMap<>(hasConfiguredDatabase 
? 3 : 4, 1F);
         result.put("ready", hasConfiguredDatabase);
@@ -139,10 +156,10 @@ public final class RuntimeStatusHandler implements 
MCPResourceHandler<MCPDatabas
     }
     
     private List<Map<String, Object>> createNextActions(final boolean 
hasConfiguredDatabase) {
-        Map<String, Object> capabilityAction = 
MCPNextActionUtils.readResource("shardingsphere://capabilities", "Read the full 
capability catalog before choosing tools.");
         if (hasConfiguredDatabase) {
-            return List.of();
+            return 
List.of(MCPNextActionUtils.readResource("shardingsphere://databases", "Read 
logical databases before choosing a database scope."));
         }
+        Map<String, Object> capabilityAction = 
MCPNextActionUtils.readResource("shardingsphere://capabilities", "Read the full 
capability catalog before choosing tools.");
         return MCPNextActionUtils.ordered(capabilityAction, 
MCPNextActionUtils.dependsOn(MCPNextActionUtils.askUser(
                 "Ask the operator to configure at least one runtimeDatabases 
entry before metadata discovery or SQL execution.", 
List.of("runtimeDatabases")), 1));
     }
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandler.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandler.java
index 05db85dacbb..ff76baecabc 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandler.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandler.java
@@ -78,6 +78,7 @@ public final class MetadataResourceHandler implements 
MCPResourceHandler<MCPData
         }
         List<?> returnedItems = capListItems(items);
         appendListSizeMetadata(navigationPayload, items.size(), 
returnedItems.size());
+        navigationPayload.put(MCPPayloadFieldNames.SUMMARY, 
createListSummary(metadata, items.size(), returnedItems.size()));
         if (items.isEmpty()) {
             appendEmptyStateGuidance(navigationPayload, metadata, 
databaseContext, uriVariables);
         } else if (isTruncated(items, returnedItems)) {
@@ -105,8 +106,9 @@ public final class MetadataResourceHandler implements 
MCPResourceHandler<MCPData
     }
     
     private Map<String, Object> createDetailPayload(final 
ShardingSphereMCPResourceMetadata descriptor, final List<?> items, final 
Map<String, Object> navigationPayload) {
-        Map<String, Object> result = new 
LinkedHashMap<>(navigationPayload.size() + 6, 1F);
+        Map<String, Object> result = new 
LinkedHashMap<>(navigationPayload.size() + 7, 1F);
         result.put("response_mode", MCPResponseMode.DETAIL);
+        result.put(MCPPayloadFieldNames.SUMMARY, 
createDetailSummary(descriptor, items));
         result.put(MCPPayloadFieldNames.RESOURCE_KIND, "detail");
         if (null != descriptor.getObjectScope()) {
             result.put("object_scope", descriptor.getObjectScope());
@@ -121,6 +123,20 @@ public final class MetadataResourceHandler implements 
MCPResourceHandler<MCPData
         return result;
     }
     
+    private String createListSummary(final ShardingSphereMCPResourceMetadata 
descriptor, final int totalCount, final int returnedCount) {
+        return String.format("Returned %d of %d %s metadata entries.", 
returnedCount, totalCount, resolveSummaryScope(descriptor));
+    }
+    
+    private String createDetailSummary(final ShardingSphereMCPResourceMetadata 
descriptor, final List<?> items) {
+        return items.isEmpty()
+                ? String.format("No %s detail item matched this resource 
URI.", resolveSummaryScope(descriptor))
+                : String.format("Returned %s detail for this resource URI.", 
resolveSummaryScope(descriptor));
+    }
+    
+    private String resolveSummaryScope(final ShardingSphereMCPResourceMetadata 
descriptor) {
+        return null == descriptor.getObjectScope() ? 
descriptor.getResourceKind() : descriptor.getObjectScope().replace('_', '-');
+    }
+    
     private void appendEmptyStateGuidance(final Map<String, Object> payload, 
final ShardingSphereMCPResourceMetadata descriptor,
                                           final MCPDatabaseHandlerContext 
databaseContext, final MCPUriVariables uriVariables) {
         Map<String, Object> emptyState = new LinkedHashMap<>(4, 1F);
@@ -270,10 +286,14 @@ public final class MetadataResourceHandler implements 
MCPResourceHandler<MCPData
     }
     
     private Map<String, Object> createNavigationPayload(final 
MCPResourceDescriptor descriptor, final MCPUriVariables uriVariables) {
-        Map<String, Object> result = new LinkedHashMap<>(3, 1F);
+        Map<String, Object> result = new LinkedHashMap<>(4, 1F);
         String uriTemplate = descriptor.getUriTemplate();
         Optional<String> selfUri = new 
MCPUriTemplate(uriTemplate).expandIfComplete(uriVariables);
-        selfUri.ifPresent(optional -> result.put("self_uri", optional));
+        selfUri.ifPresent(uri -> {
+            result.put("self_uri", uri);
+            result.put(MCPPayloadFieldNames.SELF_RESOURCE,
+                    MCPResourceHintUtils.create(uri, resolveResourceKind(uri), 
"inspect_self", "Read this metadata resource.", 
MCPPayloadFieldNames.SELF_RESOURCE));
+        });
         String parentUri = createParentUri(selfUri.orElse(""));
         if (!parentUri.isEmpty()) {
             result.put(MCPPayloadFieldNames.PARENT_RESOURCE, 
MCPResourceHintUtils.create(parentUri, resolveResourceKind(parentUri), 
"inspect_parent",
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandler.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandler.java
index 8fda589efc0..6c287fa9cd4 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandler.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandler.java
@@ -103,7 +103,7 @@ public final class ExecuteUpdateToolHandler implements 
MCPToolHandler<MCPDatabas
     }
     
     private MCPResponse createPreviewResponse(final MCPToolArguments 
toolArguments, final ClassificationResult classificationResult) {
-        Map<String, Object> result = new LinkedHashMap<>(16, 1F);
+        Map<String, Object> result = new LinkedHashMap<>(17, 1F);
         result.put("response_mode", MCPResponseMode.PREVIEW);
         result.put("result_kind", RESULT_KIND_PREVIEW);
         result.put(MCPPayloadFieldNames.EXECUTION_MODE, 
EXECUTION_MODE_PREVIEW);
@@ -118,7 +118,9 @@ public final class ExecuteUpdateToolHandler implements 
MCPToolHandler<MCPDatabas
         classificationResult.getTargetObjectName().ifPresent(optional -> 
result.put("target_object", optional));
         classificationResult.getSavepointName().ifPresent(optional -> 
result.put("savepoint", optional));
         result.put("review_guidance", PREVIEW_REVIEW_GUIDANCE);
-        result.put("review_summary", 
createReviewSummary(classificationResult));
+        String reviewSummary = createReviewSummary(classificationResult);
+        result.put(MCPPayloadFieldNames.SUMMARY, reviewSummary);
+        result.put("review_summary", reviewSummary);
         Map<String, Object> suggestedArguments = 
createSuggestedArguments(toolArguments, classificationResult);
         result.put("suggested_arguments", suggestedArguments);
         result.put(MCPPayloadFieldNames.RESOURCES_TO_READ, 
createResourcesToRead(toolArguments));
diff --git 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandler.java
 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandler.java
index f17566f8ead..491a2cf1635 100644
--- 
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandler.java
+++ 
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandler.java
@@ -74,7 +74,8 @@ public final class SearchMetadataToolHandler implements 
MCPToolHandler<MCPDataba
     }
     
     private Map<String, Object> createSearchPayloadMetadata(final 
MCPDatabaseHandlerContext databaseContext, final MetadataSearchRequest request, 
final MetadataSearchResult searchResult) {
-        Map<String, Object> result = new LinkedHashMap<>(8, 1F);
+        Map<String, Object> result = new LinkedHashMap<>(9, 1F);
+        result.put(MCPPayloadFieldNames.SUMMARY, createSummary(searchResult));
         result.put("search_context", searchResult.getSearchContext());
         result.put("total_match_count", searchResult.getTotalMatchCount());
         result.put("returned_count", searchResult.getReturnedCount());
@@ -98,6 +99,10 @@ public final class SearchMetadataToolHandler implements 
MCPToolHandler<MCPDataba
         return result;
     }
     
+    private String createSummary(final MetadataSearchResult searchResult) {
+        return String.format("Metadata search returned %d of %d matches.", 
searchResult.getReturnedCount(), searchResult.getTotalMatchCount());
+    }
+    
     private Map<String, Object> createLargeResultGuidance(final 
MetadataSearchResult searchResult) {
         Map<String, Object> result = new LinkedHashMap<>(4, 1F);
         result.put("state", "metadata_search_result_truncated");
diff --git 
a/mcp/core/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-core.yaml
 
b/mcp/core/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-core.yaml
index 2d530077ec8..9e71ad67b87 100644
--- 
a/mcp/core/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-core.yaml
+++ 
b/mcp/core/src/main/resources/META-INF/shardingsphere-mcp/mcp-descriptors/mcp-descriptor-core.yaml
@@ -884,6 +884,9 @@ tools:
           description: "Stable response mode marker for metadata search 
payloads."
           enum:
             - search
+        summary:
+          type: string
+          description: "Short model-facing metadata search result summary."
         items:
           type: array
           description: "Matched metadata objects with logical path fields, 
object type, and direct resource navigation hints when derivable."
@@ -1036,6 +1039,7 @@ tools:
                 description: "Action order values that must complete before 
this action."
       examples:
         - response_mode: search
+          summary: Metadata search returned 1 of 1 matches.
           items:
             - database: logic_db
               schema: public
@@ -1109,6 +1113,9 @@ tools:
           description: "Stable response mode marker for runtime database 
validation payloads."
           enum:
             - validation
+        summary:
+          type: string
+          description: "Short model-facing runtime database validation 
summary."
         status:
           type: string
           description: "Overall preflight status: ready when all required 
checks passed, failed otherwise."
@@ -1147,8 +1154,39 @@ tools:
         recovery:
           type: object
           description: "Structured runtime recovery payload. Empty when status 
is ready."
+        next_actions:
+          type: array
+          description: "Top-level structured follow-up actions copied from 
recovery when validation fails."
+          items:
+            type: object
+            properties:
+              order:
+                type: integer
+                description: "1-based action order."
+              type:
+                type: string
+                description: "Canonical action type such as tool_call, 
resource_read, ask_user, completion, or terminal."
+              title:
+                type: string
+                description: "Short model-facing action title."
+              resource_uri:
+                type: string
+                description: "Canonical resource URI when type is 
resource_read."
+              question:
+                type: string
+                description: "Question to ask the user when type is ask_user."
+              required_inputs:
+                type: array
+                description: "User inputs needed before continuing."
+              reason:
+                type: string
+                description: "Why the model should take this action."
+              depends_on:
+                type: array
+                description: "Action order values that must complete before 
this action."
       examples:
         - response_mode: validation
+          summary: Runtime database `logic_db` passed validation.
           status: ready
           database: logic_db
           checks:
@@ -1229,6 +1267,9 @@ tools:
           description: "Stable response mode marker for read-only SQL results."
           enum:
             - query
+        summary:
+          type: string
+          description: "Short model-facing SQL execution result summary."
         result_kind:
           type: string
           description: "Result kind. database_gateway_execute_query returns 
result_set for row results."
@@ -1305,6 +1346,7 @@ tools:
                 description: "User inputs needed before continuing."
       examples:
         - response_mode: query
+          summary: Executed SELECT statement and returned 0 row(s).
           result_kind: result_set
           statement_class: query
           statement_type: SELECT
@@ -1392,6 +1434,9 @@ tools:
           enum:
             - preview
             - executed
+        summary:
+          type: string
+          description: "Short model-facing SQL preview or execution result 
summary."
         result_kind:
           type: string
           description: "Result kind: preview, result_set, update_count, or 
statement_ack."
@@ -1521,6 +1566,7 @@ tools:
           description: "Whether returned rows were truncated."
       examples:
         - response_mode: preview
+          summary: Previewed UPDATE statement with side-effect scope 
physical-data. It has not been executed.
           result_kind: preview
           execution_mode: preview
           preview_semantics: classification_only
diff --git 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/protocol/response/MCPErrorResponseTest.java
 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/protocol/response/MCPErrorResponseTest.java
index 13ff992aa9c..9a79c0de615 100644
--- 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/protocol/response/MCPErrorResponseTest.java
+++ 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/protocol/response/MCPErrorResponseTest.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.mcp.core.protocol.response;
 
 import org.junit.jupiter.api.Test;
 
+import java.util.List;
 import java.util.Map;
 
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -35,6 +36,7 @@ class MCPErrorResponseTest {
         assertNotNull(actual.get("request_id"));
         assertFalse(actual.containsKey("recovery"));
         assertThat(actual.get("response_mode"), is("recovery"));
+        assertThat(actual.get("summary"), is("foo_message"));
         assertThat(actual.get("message"), is("foo_message"));
     }
     
@@ -48,4 +50,11 @@ class MCPErrorResponseTest {
         assertTrue((Boolean) actualRecovery.get("recoverable"));
         assertThat(actualRecovery.get("request_id"), 
is(actual.get("request_id")));
     }
+    
+    @Test
+    void assertToPayloadWithTopLevelNextActions() {
+        Map<String, Object> actual = new MCPErrorResponse("", 
Map.of("next_actions", List.of(Map.of("order", 1, "type", "terminal", "title", 
"Stop")))).toPayload();
+        assertThat(actual.get("summary"), is("Recovery guidance is 
available."));
+        assertThat(actual.get("next_actions"), is(List.of(Map.of("order", 1, 
"type", "terminal", "title", "Stop"))));
+    }
 }
diff --git 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/RuntimeStatusHandlerTest.java
 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/RuntimeStatusHandlerTest.java
index 0e6d2da70ea..c24f47d759c 100644
--- 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/RuntimeStatusHandlerTest.java
+++ 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/RuntimeStatusHandlerTest.java
@@ -38,10 +38,12 @@ class RuntimeStatusHandlerTest {
         try (MCPRequestScope requestScope = new 
MCPRequestScope(ResourceTestDataFactory.createRuntimeContext(ResourceTestDataFactory.createDatabaseMetadata(),
 "http"))) {
             Map<String, Object> actual = new 
RuntimeStatusHandler().handle(requestScope, new 
MCPUriVariables(Map.of())).toPayload();
             assertThat(actual.get("response_mode"), is("runtime"));
+            assertThat(actual.get("summary"), is("Runtime is ready with 3 
configured logical database(s)."));
             assertThat(actual.get("server_status"), is("ready"));
             assertThat(actual.get("status"), is("available"));
             assertThat(actual.get("transport"), is("http"));
             assertThat(actual.get("active_transport"), is("http"));
+            assertThat(((Map<?, ?>) 
actual.get("transport_security_summary")).get("recommended_exposure"), 
is("loopback_or_trusted_gateway"));
             assertThat(actual.get("configured_database_count"), is(3));
             assertTrue(((List<?>) actual.get("databases")).stream().map(each 
-> ((Map<?, ?>) each).get("database")).anyMatch("logic_db"::equals));
             assertThat(((Map<?, ?>) 
actual.get("redaction_summary")).get("marker"), is("******"));
@@ -50,7 +52,7 @@ class RuntimeStatusHandlerTest {
             assertFalse(actual.containsKey("capability_fingerprint"));
             assertRuntimeCapability((List<?>) actual.get("databases"), 
"logic_db");
             assertThat(extractResourceUris((List<?>) 
actual.get("resources_to_read")), is(List.of("shardingsphere://capabilities", 
"shardingsphere://databases")));
-            assertThat(actual.get("next_actions"), is(List.of()));
+            assertThat(((Map<?, ?>) ((List<?>) 
actual.get("next_actions")).getFirst()).get("resource_uri"), 
is("shardingsphere://databases"));
         }
     }
     
@@ -60,6 +62,7 @@ class RuntimeStatusHandlerTest {
             Map<String, Object> actual = new 
RuntimeStatusHandler().handle(requestScope, new 
MCPUriVariables(Map.of())).toPayload();
             assertThat(actual.get("transport"), is("stdio"));
             assertThat(actual.get("active_transport"), is("stdio"));
+            assertThat(((Map<?, ?>) 
actual.get("transport_security_summary")).get("recommended_exposure"), 
is("local_stdio_session"));
         }
     }
     
@@ -68,6 +71,7 @@ class RuntimeStatusHandlerTest {
         try (MCPRequestScope requestScope = new 
MCPRequestScope(ResourceTestDataFactory.createRuntimeContext(List.of(), 
"http"))) {
             Map<String, Object> actual = new 
RuntimeStatusHandler().handle(requestScope, new 
MCPUriVariables(Map.of())).toPayload();
             assertThat(actual.get("server_status"), 
is("configuration_required"));
+            assertThat(actual.get("summary"), is("Runtime requires at least 
one configured logical database before metadata discovery or SQL execution."));
             assertThat(actual.get("status"), is("configuration_required"));
             assertThat(actual.get("configured_database_count"), is(0));
             assertThat(((List<?>) actual.get("databases")).size(), is(0));
diff --git 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/ServerCapabilitiesHandlerTest.java
 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/ServerCapabilitiesHandlerTest.java
index 8df63f7df58..fb64372c4bf 100644
--- 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/ServerCapabilitiesHandlerTest.java
+++ 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/capability/ServerCapabilitiesHandlerTest.java
@@ -122,6 +122,10 @@ class ServerCapabilitiesHandlerTest {
                 is("shardingsphere://guidance complements MCP list methods 
with ShardingSphere domain guidance, workflow guidance, and side-effect 
notes."));
         assertThat(actual.get("guidance_resource"), 
is("shardingsphere://guidance"));
         assertFalse(actual.containsKey("safe_first_resource"));
+        Map<?, ?> firstRoute = findByKey((List<?>) 
actual.get("first_call_routes"), "intent", "inspect_metadata");
+        assertThat(firstRoute.get("first_action"), is("read_resource 
shardingsphere://databases"));
+        Map<?, ?> recoveryRoute = findByKey((List<?>) 
actual.get("first_call_routes"), "intent", "recover_error");
+        assertThat(recoveryRoute.get("first_action"), is("follow top-level 
next_actions"));
         Map<?, ?> metadataRule = (Map<?, ?>) actual.get("metadata_rule");
         assertThat(metadataRule.get("first_resource"), 
is("shardingsphere://databases"));
         assertThat(metadataRule.get("search_tool"), 
is("database_gateway_search_metadata"));
@@ -247,6 +251,7 @@ class ServerCapabilitiesHandlerTest {
     private void assertCoreToolSchemas(final Map<String, Object> capabilities) 
{
         Map<?, ?> searchMetadataTool = findTool(capabilities, 
"database_gateway_search_metadata");
         Map<?, ?> searchMetadataOutputProperties = (Map<?, ?>) ((Map<?, ?>) 
searchMetadataTool.get("outputSchema")).get("properties");
+        assertTrue(searchMetadataOutputProperties.containsKey("summary"));
         
assertTrue(searchMetadataOutputProperties.containsKey("total_match_count"));
         
assertTrue(searchMetadataOutputProperties.containsKey("returned_count"));
         assertTrue(searchMetadataOutputProperties.containsKey("truncated"));
@@ -256,13 +261,16 @@ class ServerCapabilitiesHandlerTest {
         Map<?, ?> validateRuntimeDatabaseTool = findTool(capabilities, 
"database_gateway_validate_runtime_database");
         Map<?, ?> validateRuntimeDatabaseOutputProperties = (Map<?, ?>) 
((Map<?, ?>) validateRuntimeDatabaseTool.get("outputSchema")).get("properties");
         assertThat(getInputFieldNames(validateRuntimeDatabaseTool), 
is(List.of("database")));
+        
assertTrue(validateRuntimeDatabaseOutputProperties.containsKey("summary"));
         
assertTrue(validateRuntimeDatabaseOutputProperties.containsKey("status"));
         
assertTrue(validateRuntimeDatabaseOutputProperties.containsKey("checks"));
         
assertTrue(validateRuntimeDatabaseOutputProperties.containsKey("category"));
         
assertTrue(validateRuntimeDatabaseOutputProperties.containsKey("recovery"));
+        
assertTrue(validateRuntimeDatabaseOutputProperties.containsKey("next_actions"));
         Map<?, ?> executeUpdateTool = findTool(capabilities, 
"database_gateway_execute_update");
         Map<?, ?> executeUpdateOutputProperties = (Map<?, ?>) ((Map<?, ?>) 
executeUpdateTool.get("outputSchema")).get("properties");
         assertTrue(executeUpdateOutputProperties.containsKey("response_mode"));
+        assertTrue(executeUpdateOutputProperties.containsKey("summary"));
         assertTrue(((List<?>) ((Map<?, ?>) 
executeUpdateOutputProperties.get("result_kind")).get("enum")).containsAll(List.of("preview",
 "result_set", "update_count", "statement_ack")));
         
assertTrue(executeUpdateOutputProperties.containsKey("preview_semantics"));
         
assertTrue(executeUpdateOutputProperties.containsKey("review_summary"));
diff --git 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
index 78c12abaa59..5b6f43c21bf 100644
--- 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
+++ 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
@@ -53,8 +53,20 @@ class MetadataResourceHandlerTest {
         MetadataResourceHandler handler = new 
MetadataResourceHandler("shardingsphere://databases",
                 (requestContext, uriVariables) -> List.of(Map.of("database", 
"logic_db")));
         MCPResponse actual = 
handler.handle(mock(MCPDatabaseHandlerContext.class), new 
MCPUriVariables(Map.of()));
-        assertThat(actual.toPayload(), is(Map.of("response_mode", "list", 
"items", List.of(Map.of("database", "logic_db")), "count", 1, "has_more", false,
-                "continuation_mode", "none", "self_uri", 
"shardingsphere://databases", "total_count", 1, "returned_count", 1, 
"truncated", false)));
+        Map<String, Object> actualPayload = actual.toPayload();
+        assertThat(actualPayload.get("response_mode"), is("list"));
+        assertThat(actualPayload.get("summary"), is("Returned 1 of 1 
logical-database metadata entries."));
+        assertThat(actualPayload.get("items"), is(List.of(Map.of("database", 
"logic_db"))));
+        assertThat(actualPayload.get("count"), is(1));
+        assertFalse((Boolean) actualPayload.get("has_more"));
+        assertThat(actualPayload.get("continuation_mode"), is("none"));
+        assertThat(actualPayload.get("self_uri"), 
is("shardingsphere://databases"));
+        assertThat(((Map<?, ?>) 
actualPayload.get("self_resource")).get("uri"), 
is("shardingsphere://databases"));
+        assertThat(((Map<?, ?>) 
actualPayload.get("self_resource")).get("resource_kind"), 
is("logical-database"));
+        assertThat(((Map<?, ?>) 
actualPayload.get("self_resource")).get("source_field"), is("self_resource"));
+        assertThat(actualPayload.get("total_count"), is(1));
+        assertThat(actualPayload.get("returned_count"), is(1));
+        assertFalse((Boolean) actualPayload.get("truncated"));
     }
     
     @Test
@@ -66,6 +78,7 @@ class MetadataResourceHandlerTest {
         assertThat(actualPayload.get("count"), is(100));
         assertThat(actualPayload.get("total_count"), is(101));
         assertThat(actualPayload.get("returned_count"), is(100));
+        assertThat(actualPayload.get("summary"), is("Returned 100 of 101 
logical-database metadata entries."));
         assertTrue((Boolean) actualPayload.get("truncated"));
         assertTrue((Boolean) actualPayload.get("has_more"));
         assertThat(actualPayload.get("continuation_mode"), 
is("metadata_search"));
@@ -137,26 +150,36 @@ class MetadataResourceHandlerTest {
                 (requestContext, uriVariables) -> List.of(Map.of("database", 
uriVariables.getValue("database"))));
         MCPUriVariables uriVariables = new MCPUriVariables(Map.of("database", 
"逻辑 库/2026?"));
         MCPResponse actual = 
handler.handle(mock(MCPDatabaseHandlerContext.class), uriVariables);
-        assertThat(actual.toPayload(), is(Map.of("response_mode", "detail", 
"resource_kind", "detail", "object_scope", "logical-database", "found", true,
-                "items", List.of(Map.of("database", "逻辑 库/2026?")), "count", 
1, "item", Map.of("database", "逻辑 库/2026?"),
-                "self_uri", 
"shardingsphere://databases/%E9%80%BB%E8%BE%91%20%E5%BA%93%2F2026%3F",
-                "parent_resource", Map.of("uri", "shardingsphere://databases", 
"resource_kind", "logical-database", "purpose", "inspect_parent",
-                        "reason", "Read the parent metadata resource before 
broadening or correcting the request.", "source_field", "parent_resource"),
-                "next_resources", List.of(Map.of("uri", 
"shardingsphere://databases/%E9%80%BB%E8%BE%91%20%E5%BA%93%2F2026%3F/schemas", 
"resource_kind", "schema", "purpose", "inspect_detail",
-                        "reason", "List schemas after choosing a logical 
database.", "source_field", "next_resources"),
-                        Map.of("uri", 
"shardingsphere://databases/%E9%80%BB%E8%BE%91%20%E5%BA%93%2F2026%3F/storage-units",
 "resource_kind", "storage-unit",
-                                "purpose", "inspect_detail", "reason", "List 
storage units after choosing a logical database.", "source_field", 
"next_resources"),
-                        Map.of("uri", 
"shardingsphere://databases/%E9%80%BB%E8%BE%91%20%E5%BA%93%2F2026%3F/single-tables",
 "resource_kind", "single-table",
-                                "purpose", "inspect_detail", "reason", "List 
single table mappings after choosing a logical database.", "source_field", 
"next_resources"),
-                        Map.of("uri", 
"shardingsphere://databases/%E9%80%BB%E8%BE%91%20%E5%BA%93%2F2026%3F/single-table/default-storage-unit",
-                                "resource_kind", "single-table", "purpose", 
"inspect_detail",
-                                "reason", "Read the default single table 
storage unit after choosing a logical database.", "source_field", 
"next_resources")))));
+        Map<String, Object> actualPayload = actual.toPayload();
+        assertThat(actualPayload.get("response_mode"), is("detail"));
+        assertThat(actualPayload.get("summary"), is("Returned logical-database 
detail for this resource URI."));
+        assertThat(actualPayload.get("resource_kind"), is("detail"));
+        assertThat(actualPayload.get("object_scope"), is("logical-database"));
+        assertTrue((Boolean) actualPayload.get("found"));
+        assertThat(actualPayload.get("items"), is(List.of(Map.of("database", 
"逻辑 库/2026?"))));
+        assertThat(actualPayload.get("count"), is(1));
+        assertThat(actualPayload.get("item"), is(Map.of("database", "逻辑 
库/2026?")));
+        String expectedSelfUri = 
"shardingsphere://databases/%E9%80%BB%E8%BE%91%20%E5%BA%93%2F2026%3F";
+        assertThat(actualPayload.get("self_uri"), is(expectedSelfUri));
+        assertThat(((Map<?, ?>) 
actualPayload.get("self_resource")).get("uri"), is(expectedSelfUri));
+        Map<?, ?> actualParentResource = (Map<?, ?>) 
actualPayload.get("parent_resource");
+        assertThat(actualParentResource.get("uri"), 
is("shardingsphere://databases"));
+        assertThat(actualParentResource.get("source_field"), 
is("parent_resource"));
+        List<?> actualNextResources = (List<?>) 
actualPayload.get("next_resources");
+        assertThat(actualNextResources.size(), is(4));
+        List<String> actualNextResourceUris = 
actualNextResources.stream().map(each -> (String) ((Map<?, ?>) 
each).get("uri")).toList();
+        assertThat(actualNextResourceUris, is(List.of(
+                expectedSelfUri + "/schemas",
+                expectedSelfUri + "/storage-units",
+                expectedSelfUri + "/single-tables",
+                expectedSelfUri + "/single-table/default-storage-unit")));
     }
     
     @Test
     void assertHandleMissingDetailResource() {
         MetadataResourceHandler handler = new 
MetadataResourceHandler("shardingsphere://databases/{database}", 
(requestContext, uriVariables) -> List.of());
         MCPResponse actual = 
handler.handle(mock(MCPDatabaseHandlerContext.class), 
mock(MCPUriVariables.class));
+        assertThat(actual.toPayload().get("summary"), is("No logical-database 
detail item matched this resource URI."));
         assertFalse((Boolean) actual.toPayload().get("found"));
         assertThat(((Map<?, ?>) 
actual.toPayload().get("empty_state")).get("reason"), is("logical-database 
detail resource was not found for this URI."));
         assertThat(((Map<?, ?>) 
actual.toPayload().get("recovery")).get("recovery_category"), is("not_found"));
diff --git 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandlerTest.java
 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandlerTest.java
index eced0f748b7..cf636605810 100644
--- 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandlerTest.java
+++ 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteUpdateToolHandlerTest.java
@@ -113,6 +113,7 @@ class ExecuteUpdateToolHandlerTest {
         assertThat(actual.toPayload().get("status"), is("PREVIEWED"));
         assertThat(actual.toPayload().get("statement_class"), is("dml"));
         assertThat(actual.toPayload().get("side_effect_scope"), 
is(List.of("physical-data")));
+        assertThat(actual.toPayload().get("summary"), is("Previewed UPDATE 
statement with side-effect scope physical-data. It has not been executed."));
         assertThat(actual.toPayload().get("review_summary"), is("Previewed 
UPDATE statement with side-effect scope physical-data. It has not been 
executed."));
         assertThat(actual.toPayload().get("review_guidance"),
                 is("Review normalized_sql and side_effect_scope before 
execution. "
diff --git 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandlerTest.java
 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandlerTest.java
index c6cede663a6..f0f645f65cd 100644
--- 
a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandlerTest.java
+++ 
b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataToolHandlerTest.java
@@ -70,6 +70,7 @@ class SearchMetadataToolHandlerTest {
         assertTrue(actualItemProperties.containsKey("matched_fields"));
         assertTrue(actualItemProperties.containsKey("matched_value"));
         assertTrue(actualProperties.containsKey("search_context"));
+        assertTrue(actualProperties.containsKey("summary"));
         assertTrue(actualProperties.containsKey("total_match_count"));
         assertTrue(actualProperties.containsKey("returned_count"));
         assertTrue(actualProperties.containsKey("truncated"));
@@ -88,6 +89,7 @@ class SearchMetadataToolHandlerTest {
             Map<String, Object> actualPayload = actual.toPayload();
             assertThat(actual, isA(MCPItemsResponse.class));
             assertThat(((List<?>) actualPayload.get("items")).size(), is(1));
+            assertThat(actualPayload.get("summary"), is("Metadata search 
returned 1 of 1 matches."));
             assertThat(actualPayload.get("total_match_count"), is(1));
             assertThat(actualPayload.get("returned_count"), is(1));
             assertFalse((Boolean) actualPayload.get("truncated"));
@@ -164,6 +166,7 @@ class SearchMetadataToolHandlerTest {
                     Map.of("database", "large_db", "object_types", 
List.of(SupportedMCPMetadataObjectType.TABLE.name()))));
             Map<String, Object> actualPayload = actual.toPayload();
             assertThat(((List<?>) actualPayload.get("items")).size(), is(100));
+            assertThat(actualPayload.get("summary"), is("Metadata search 
returned 100 of 101 matches."));
             assertThat(actualPayload.get("total_match_count"), is(101));
             assertThat(actualPayload.get("returned_count"), is(100));
             assertTrue((Boolean) actualPayload.get("truncated"));
@@ -204,6 +207,7 @@ class SearchMetadataToolHandlerTest {
         try (MCPRequestScope requestContext = new 
MCPRequestScope(createSearchRuntimeContext())) {
             MCPResponse actual = new 
SearchMetadataToolHandler().handle(requestContext, new MCPToolCall("session-1", 
Map.of("database", "logic_db", "query", "missing")));
             Map<String, Object> actualPayload = actual.toPayload();
+            assertThat(actualPayload.get("summary"), is("Metadata search 
returned 0 of 0 matches."));
             assertThat(((Map<?, ?>) 
actualPayload.get("empty_state")).get("state"), is("no_match"));
             assertThat(((Map<?, ?>) 
actualPayload.get("empty_state")).get("category"), is("object_not_visible"));
             Map<?, ?> actualNextAction = (Map<?, ?>) ((List<?>) 
actualPayload.get("next_actions")).getFirst();
diff --git 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/response/RuntimeDatabaseValidationResult.java
 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/response/RuntimeDatabaseValidationResult.java
index 10eb4cf62e3..d689df4addb 100644
--- 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/response/RuntimeDatabaseValidationResult.java
+++ 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/response/RuntimeDatabaseValidationResult.java
@@ -73,16 +73,26 @@ public final class RuntimeDatabaseValidationResult 
implements MCPResponse {
     
     @Override
     public Map<String, Object> toPayload() {
-        Map<String, Object> result = new LinkedHashMap<>(6, 1F);
+        Map<String, Object> result = new LinkedHashMap<>(8, 1F);
         result.put("response_mode", MCPResponseMode.VALIDATION);
+        result.put(MCPPayloadFieldNames.SUMMARY, createSummary());
         result.put("status", status);
         result.put("database", database);
         result.put("checks", createChecksPayload());
         result.put("category", category);
         result.put(MCPPayloadFieldNames.RECOVERY, recovery);
+        if (recovery.containsKey(MCPPayloadFieldNames.NEXT_ACTIONS)) {
+            result.put(MCPPayloadFieldNames.NEXT_ACTIONS, 
recovery.get(MCPPayloadFieldNames.NEXT_ACTIONS));
+        }
         return result;
     }
     
+    private String createSummary() {
+        return "ready".equals(status)
+                ? String.format("Runtime database `%s` passed validation.", 
database)
+                : String.format("Runtime database `%s` failed validation with 
category `%s`.", database, category);
+    }
+    
     private List<Map<String, Object>> createChecksPayload() {
         List<Map<String, Object>> result = new LinkedList<>();
         for (RuntimeDatabaseValidationCheckResult each : checks) {
diff --git 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/response/SQLExecutionResponse.java
 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/response/SQLExecutionResponse.java
index 9a3ee3a0ddb..6f7492f0ba4 100644
--- 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/response/SQLExecutionResponse.java
+++ 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/response/SQLExecutionResponse.java
@@ -174,7 +174,7 @@ public final class SQLExecutionResponse implements 
MCPResponse {
     
     @Override
     public Map<String, Object> toPayload() {
-        Map<String, Object> result = new LinkedHashMap<>(32, 1F);
+        Map<String, Object> result = new LinkedHashMap<>(33, 1F);
         result.put("response_mode", responseMode);
         result.put("result_kind", 
resultKind.name().toLowerCase(Locale.ENGLISH));
         if (!executionMode.isEmpty()) {
@@ -183,6 +183,7 @@ public final class SQLExecutionResponse implements 
MCPResponse {
         result.put("statement_class", 
statementClass.name().toLowerCase(Locale.ENGLISH));
         result.put("statement_type", statementType);
         result.put("status", status);
+        result.put(MCPPayloadFieldNames.SUMMARY, createSummary());
         if (!normalizedSql.isEmpty()) {
             result.put("normalized_sql", normalizedSql);
         }
@@ -205,6 +206,19 @@ public final class SQLExecutionResponse implements 
MCPResponse {
         return result;
     }
     
+    private String createSummary() {
+        return switch (resultKind) {
+            case RESULT_SET -> createResultSetSummary();
+            case UPDATE_COUNT -> String.format("Executed %s statement and 
affected %d row(s).", statementType, affectedRows);
+            case STATEMENT_ACK -> message.isEmpty() ? String.format("Executed 
%s statement.", statementType) : message;
+        };
+    }
+    
+    private String createResultSetSummary() {
+        String result = String.format("Executed %s statement and returned %d 
row(s).", statementType, rows.size());
+        return truncated ? result + " Result was truncated." : result;
+    }
+    
     private List<Map<String, Object>> createNextActions() {
         if (ExecuteQueryResultKind.RESULT_SET == resultKind && truncated) {
             return List.of(MCPNextActionUtils.askUser("The result was 
truncated by max_rows. Ask for a narrower SELECT, stronger WHERE clause, or 
smaller projection before retrying.",
diff --git 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilder.java
 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilder.java
index d17f550048d..8f013b469a7 100644
--- 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilder.java
+++ 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilder.java
@@ -66,11 +66,12 @@ final class MCPGuidancePayloadBuilder {
     }
     
     Map<String, Object> createModelFirstSummary() {
-        Map<String, Object> result = new LinkedHashMap<>(11, 1F);
+        Map<String, Object> result = new LinkedHashMap<>(12, 1F);
         result.put("official_discovery_methods", 
createOfficialDiscoveryMethods());
         result.put("argument_completion_method", ARGUMENT_COMPLETION_METHOD);
         result.put("guidance_resource_role", GUIDANCE_RESOURCE_URI + " 
complements MCP list methods with ShardingSphere domain guidance, workflow 
guidance, and side-effect notes.");
         result.put("guidance_resource", GUIDANCE_RESOURCE_URI);
+        result.put("first_call_routes", createFirstCallRoutes());
         result.put("metadata_rule", createMetadataRule());
         result.put("preflight_rule", createPreflightRule());
         result.put("sql_tool_selection", createSqlToolSelection());
@@ -81,6 +82,29 @@ final class MCPGuidancePayloadBuilder {
         return result;
     }
     
+    private List<Map<String, Object>> createFirstCallRoutes() {
+        return List.of(
+                createFirstCallRoute("inspect_metadata", "read_resource 
shardingsphere://databases", "call_tool database_gateway_search_metadata or 
read returned resource.uri",
+                        "Stop after the requested detail resource is read."),
+                createFirstCallRoute("validate_runtime", "read_resource 
shardingsphere://runtime", "call_tool 
database_gateway_validate_runtime_database with a configured database",
+                        "Follow top-level next_actions when validation 
fails."),
+                createFirstCallRoute("read_only_sql", "read_resource 
shardingsphere://databases/{database}/capabilities", "call_tool 
database_gateway_execute_query",
+                        "Stop after reporting the result rows."),
+                createFirstCallRoute("side_effect_sql", "call_tool 
database_gateway_execute_update execution_mode=preview", "call_tool 
database_gateway_execute_update execution_mode=execute",
+                        "Execute only after preview review confirms the 
intended side effect."),
+                createFirstCallRoute("recover_error", "follow top-level 
next_actions", "fallback to recovery.next_actions when top-level actions are 
absent",
+                        "Ask the user only when no deterministic resource, 
completion, or tool action is available."));
+    }
+    
+    private Map<String, Object> createFirstCallRoute(final String intent, 
final String firstAction, final String nextStep, final String stopRule) {
+        Map<String, Object> result = new LinkedHashMap<>(4, 1F);
+        result.put("intent", intent);
+        result.put("first_action", firstAction);
+        result.put("next_step", nextStep);
+        result.put("stop_rule", stopRule);
+        return result;
+    }
+    
     Map<String, Object> createModelContract() {
         Map<String, Object> result = new LinkedHashMap<>(12, 1F);
         result.put("public_surface_source", MCP_LIST_METHODS_SOURCE);
diff --git 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPModelFacingPayloadContract.java
 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPModelFacingPayloadContract.java
index 16d5f697dc7..6ed8fc9099a 100644
--- 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPModelFacingPayloadContract.java
+++ 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPModelFacingPayloadContract.java
@@ -50,9 +50,9 @@ public final class MCPModelFacingPayloadContract {
     private static final Collection<String> NEXT_ACTION_SCHEMA_ALLOWED_FIELDS 
= createNextActionSchemaAllowedFields();
     
     private static final Collection<String> MODEL_CRITICAL_FIELD_NAMES = 
List.of(
-            MCPPayloadFieldNames.NEXT_ACTIONS, 
MCPPayloadFieldNames.RESOURCES_TO_READ, MCPPayloadFieldNames.RESOURCE, 
MCPPayloadFieldNames.PARENT_RESOURCE,
-            MCPPayloadFieldNames.NEXT_RESOURCES, "manual_artifact_summary", 
"manual_follow_up", "empty_state", "ambiguity_state", 
MCPPayloadFieldNames.RECOVERY, "recovery_guidance",
-            "remediation");
+            MCPPayloadFieldNames.SUMMARY, MCPPayloadFieldNames.NEXT_ACTIONS, 
MCPPayloadFieldNames.RESOURCES_TO_READ, MCPPayloadFieldNames.RESOURCE,
+            MCPPayloadFieldNames.SELF_RESOURCE, 
MCPPayloadFieldNames.PARENT_RESOURCE, MCPPayloadFieldNames.NEXT_RESOURCES, 
"manual_artifact_summary", "manual_follow_up",
+            "empty_state", "ambiguity_state", MCPPayloadFieldNames.RECOVERY, 
"recovery_guidance", "remediation");
     
     private MCPModelFacingPayloadContract() {
     }
diff --git 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPPayloadFieldNames.java
 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPPayloadFieldNames.java
index 8b13f8e774c..84ca83eb46d 100644
--- 
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPPayloadFieldNames.java
+++ 
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/protocol/MCPPayloadFieldNames.java
@@ -58,8 +58,12 @@ public final class MCPPayloadFieldNames {
     
     public static final String SECRET = "secret";
     
+    public static final String SELF_RESOURCE = "self_resource";
+    
     public static final String SOURCE_FIELD = "source_field";
     
+    public static final String SUMMARY = "summary";
+    
     public static final String URI = "uri";
     
     private MCPPayloadFieldNames() {
diff --git 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/response/RuntimeDatabaseValidationResultTest.java
 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/response/RuntimeDatabaseValidationResultTest.java
index 42a1007701e..2a42235c6cb 100644
--- 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/response/RuntimeDatabaseValidationResultTest.java
+++ 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/response/RuntimeDatabaseValidationResultTest.java
@@ -53,6 +53,7 @@ class RuntimeDatabaseValidationResultTest {
                 
List.of(RuntimeDatabaseValidationCheckResult.failed("metadata_read", 
"connection_failed", "Failed to read metadata.")),
                 "connection_failed", Map.of("category", 
"connection_failed")).toPayload();
         assertThat(actual.get("response_mode"), is("validation"));
+        assertThat(actual.get("summary"), is("Runtime database `logic_db` 
failed validation with category `connection_failed`."));
         assertThat(actual.get("status"), is("failed"));
         assertThat(actual.get("database"), is("logic_db"));
         assertThat(actual.get("category"), is("connection_failed"));
@@ -63,4 +64,13 @@ class RuntimeDatabaseValidationResultTest {
                 "category", "connection_failed",
                 "message", "Failed to read metadata."))));
     }
+    
+    @Test
+    void assertToPayloadWithTopLevelNextActions() {
+        Map<String, Object> recovery = Map.of("category", "connection_failed", 
"next_actions", List.of(Map.of("order", 1, "type", "resource_read", "title", 
"Read resource",
+                "resource_uri", "shardingsphere://runtime")));
+        Map<String, Object> actual = 
RuntimeDatabaseValidationResult.failed("logic_db",
+                
List.of(RuntimeDatabaseValidationCheckResult.failed("metadata_read", 
"connection_failed", "Failed to read metadata.")), "connection_failed", 
recovery).toPayload();
+        assertThat(actual.get("next_actions"), 
is(recovery.get("next_actions")));
+    }
 }
diff --git 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/response/SQLExecutionResponseTest.java
 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/response/SQLExecutionResponseTest.java
index a909764871d..2e19a2620cb 100644
--- 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/response/SQLExecutionResponseTest.java
+++ 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/response/SQLExecutionResponseTest.java
@@ -127,26 +127,27 @@ class SQLExecutionResponseTest {
                 Arguments.of("result set with rows", 
(Supplier<SQLExecutionResponse>) () -> SQLExecutionResponse.resultSet(columns, 
rows, true).withExecutionHints(10, 5000),
                         Map.ofEntries(
                                 Map.entry("result_kind", "result_set"), 
Map.entry("statement_class", "query"), Map.entry("statement_type", "SELECT"), 
Map.entry("status", "OK"),
-                                Map.entry("response_mode", "query"),
+                                Map.entry("response_mode", "query"), 
Map.entry("summary", "Executed SELECT statement and returned 1 row(s). Result 
was truncated."),
                                 Map.entry("columns", columns), 
Map.entry("rows", rows), Map.entry("row_object_status", "available"), 
Map.entry("row_objects", rowObjects),
                                 Map.entry("returned_row_count", 1), 
Map.entry("applied_max_rows", 10), Map.entry("applied_timeout_ms", 5000), 
Map.entry("truncated", true),
                                 Map.entry("next_actions", 
truncatedResultSetNextActions))),
                 Arguments.of("result set with null rows", 
(Supplier<SQLExecutionResponse>) () -> 
SQLExecutionResponse.resultSet(List.of(), null, false),
                         Map.ofEntries(
                                 Map.entry("result_kind", "result_set"), 
Map.entry("statement_class", "query"), Map.entry("statement_type", "SELECT"), 
Map.entry("status", "OK"),
-                                Map.entry("response_mode", "query"),
+                                Map.entry("response_mode", "query"), 
Map.entry("summary", "Executed SELECT statement and returned 0 row(s)."),
                                 Map.entry("columns", List.of()), 
Map.entry("rows", List.of()), Map.entry("row_object_status", "available"), 
Map.entry("row_objects", List.of()),
                                 Map.entry("returned_row_count", 0), 
Map.entry("applied_max_rows", 0), Map.entry("applied_timeout_ms", 0), 
Map.entry("truncated", false),
                                 Map.entry("next_actions", 
resultSetNextActions))),
                 Arguments.of("update count", (Supplier<SQLExecutionResponse>) 
() -> SQLExecutionResponse.updateCount("UPDATE", 2),
                         Map.ofEntries(Map.entry("response_mode", "executed"), 
Map.entry("result_kind", "update_count"), Map.entry("statement_class", "dml"),
-                                Map.entry("statement_type", "UPDATE"), 
Map.entry("status", "OK"), Map.entry("affected_rows", 2), 
Map.entry("truncated", false),
+                                Map.entry("statement_type", "UPDATE"), 
Map.entry("status", "OK"), Map.entry("summary", "Executed UPDATE statement and 
affected 2 row(s)."),
+                                Map.entry("affected_rows", 2), 
Map.entry("truncated", false),
                                 Map.entry("applied_max_rows", 0), 
Map.entry("applied_timeout_ms", 0), Map.entry("next_actions", 
executionNextActions))),
                 Arguments.of("statement acknowledgement",
                         (Supplier<SQLExecutionResponse>) () -> 
SQLExecutionResponse.statementAck(SupportedMCPStatement.TRANSACTION_CONTROL, 
"COMMIT", "Transaction committed."),
                         Map.ofEntries(Map.entry("response_mode", "executed"), 
Map.entry("result_kind", "statement_ack"), Map.entry("statement_class", 
"transaction_control"),
-                                Map.entry("statement_type", "COMMIT"), 
Map.entry("status", "OK"), Map.entry("message", "Transaction committed."), 
Map.entry("truncated", false),
-                                Map.entry("applied_max_rows", 0), 
Map.entry("applied_timeout_ms", 0), Map.entry("next_actions", 
executionNextActions))));
+                                Map.entry("statement_type", "COMMIT"), 
Map.entry("status", "OK"), Map.entry("summary", "Transaction committed."), 
Map.entry("message", "Transaction committed."),
+                                Map.entry("truncated", false), 
Map.entry("applied_max_rows", 0), Map.entry("applied_timeout_ms", 0), 
Map.entry("next_actions", executionNextActions))));
     }
     
     private static List<Map<String, Object>> createNextActions(final String 
reason) {
diff --git 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilderTest.java
 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilderTest.java
index 945f2a9acb8..be5ee710d52 100644
--- 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilderTest.java
+++ 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPGuidancePayloadBuilderTest.java
@@ -51,6 +51,10 @@ class MCPGuidancePayloadBuilderTest {
                 is("shardingsphere://guidance complements MCP list methods 
with ShardingSphere domain guidance, workflow guidance, and side-effect 
notes."));
         assertThat(actual.get("guidance_resource"), 
is("shardingsphere://guidance"));
         assertFalse(actual.containsKey("safe_first_resource"));
+        Map<?, ?> actualMetadataRoute = 
findByKey(castToRouteList(actual.get("first_call_routes")), "intent", 
"inspect_metadata");
+        assertThat(actualMetadataRoute.get("first_action"), is("read_resource 
shardingsphere://databases"));
+        Map<?, ?> actualRecoveryRoute = 
findByKey(castToRouteList(actual.get("first_call_routes")), "intent", 
"recover_error");
+        assertThat(actualRecoveryRoute.get("first_action"), is("follow 
top-level next_actions"));
         assertThat(((Map<?, ?>) actual.get("preflight_rule")).get("tool"), 
is("database_gateway_validate_runtime_database"));
         
assertThat(castToMap(castToMap(actual.get("sql_tool_selection")).get("side_effecting")).get("execute_requires"),
 is("execution_mode=execute"));
         Map<?, ?> actualWorkflowRule = castToMap(actual.get("workflow_rule"));
@@ -129,6 +133,11 @@ class MCPGuidancePayloadBuilderTest {
         return (Map<?, ?>) value;
     }
     
+    @SuppressWarnings("unchecked")
+    private List<Map<String, Object>> castToRouteList(final Object value) {
+        return (List<Map<String, Object>>) value;
+    }
+    
     private Map<String, Object> createOfficialDiscoveryMethods() {
         return Map.of("tools", "tools/list", "resources", "resources/list", 
"resource_templates", "resources/templates/list", "prompts", "prompts/list");
     }
diff --git 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/protocol/MCPModelFacingPayloadContractTest.java
 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/protocol/MCPModelFacingPayloadContractTest.java
index 09707bbec39..38ffd711f56 100644
--- 
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/protocol/MCPModelFacingPayloadContractTest.java
+++ 
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/protocol/MCPModelFacingPayloadContractTest.java
@@ -60,7 +60,9 @@ class MCPModelFacingPayloadContractTest {
     @Test
     void assertGetModelCriticalFieldNames() {
         Collection<String> actual = 
MCPModelFacingPayloadContract.getModelCriticalFieldNames();
+        assertTrue(actual.contains(MCPPayloadFieldNames.SUMMARY));
         assertTrue(actual.contains(MCPPayloadFieldNames.NEXT_ACTIONS));
+        assertTrue(actual.contains(MCPPayloadFieldNames.SELF_RESOURCE));
         assertTrue(actual.contains("manual_follow_up"));
     }
 }
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatterTest.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatterTest.java
index ce053954a62..9dd345a363b 100644
--- 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatterTest.java
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMMCPModelFacingToolResponseFormatterTest.java
@@ -90,27 +90,31 @@ class LLMMCPModelFacingToolResponseFormatterTest {
     
     @Test
     void assertFormatWithModelCriticalFields() {
-        Map<String, Object> actual = format(Map.of(
-                "resources_to_read", List.of(Map.of("uri", 
"shardingsphere://capabilities")),
-                "resource", Map.of("uri", "shardingsphere://databases"),
-                "parent_resource", Map.of("uri", "shardingsphere://databases"),
-                "next_resources", List.of(Map.of("uri", 
"shardingsphere://databases/logic_db/schemas")),
-                "manual_artifact_summary", "Review DistSQL.",
-                "manual_follow_up", "Validate runtime state.",
-                "empty_state", Map.of("state", "no_match"),
-                "recovery_guidance", "Read metadata before retrying.",
-                "remediation", "Fix the mismatch.",
-                "ignored", "value"));
-        assertThat(actual, is(Map.of(
-                "resources_to_read", List.of(Map.of("uri", 
"shardingsphere://capabilities")),
-                "resource", Map.of("uri", "shardingsphere://databases"),
-                "parent_resource", Map.of("uri", "shardingsphere://databases"),
-                "next_resources", List.of(Map.of("uri", 
"shardingsphere://databases/logic_db/schemas")),
-                "manual_artifact_summary", "Review DistSQL.",
-                "manual_follow_up", "Validate runtime state.",
-                "empty_state", Map.of("state", "no_match"),
-                "recovery_guidance", "Read metadata before retrying.",
-                "remediation", "Fix the mismatch.")));
+        Map<String, Object> actual = format(Map.ofEntries(
+                Map.entry("summary", "Read logical databases first."),
+                Map.entry("resources_to_read", List.of(Map.of("uri", 
"shardingsphere://capabilities"))),
+                Map.entry("resource", Map.of("uri", 
"shardingsphere://databases")),
+                Map.entry("self_resource", Map.of("uri", 
"shardingsphere://databases")),
+                Map.entry("parent_resource", Map.of("uri", 
"shardingsphere://databases")),
+                Map.entry("next_resources", List.of(Map.of("uri", 
"shardingsphere://databases/logic_db/schemas"))),
+                Map.entry("manual_artifact_summary", "Review DistSQL."),
+                Map.entry("manual_follow_up", "Validate runtime state."),
+                Map.entry("empty_state", Map.of("state", "no_match")),
+                Map.entry("recovery_guidance", "Read metadata before 
retrying."),
+                Map.entry("remediation", "Fix the mismatch."),
+                Map.entry("ignored", "value")));
+        assertThat(actual, is(Map.ofEntries(
+                Map.entry("summary", "Read logical databases first."),
+                Map.entry("resources_to_read", List.of(Map.of("uri", 
"shardingsphere://capabilities"))),
+                Map.entry("resource", Map.of("uri", 
"shardingsphere://databases")),
+                Map.entry("self_resource", Map.of("uri", 
"shardingsphere://databases")),
+                Map.entry("parent_resource", Map.of("uri", 
"shardingsphere://databases")),
+                Map.entry("next_resources", List.of(Map.of("uri", 
"shardingsphere://databases/logic_db/schemas"))),
+                Map.entry("manual_artifact_summary", "Review DistSQL."),
+                Map.entry("manual_follow_up", "Validate runtime state."),
+                Map.entry("empty_state", Map.of("state", "no_match")),
+                Map.entry("recovery_guidance", "Read metadata before 
retrying."),
+                Map.entry("remediation", "Fix the mismatch."))));
     }
     
     @Test
diff --git 
a/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/guidance.yaml
 
b/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/guidance.yaml
index 7ddad2afebd..b227826ebaa 100644
--- 
a/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/guidance.yaml
+++ 
b/test/e2e/mcp/src/test/resources/baseline-contract/model-contract/guidance.yaml
@@ -24,6 +24,23 @@ model_first_summary:
   guidance_resource_role: shardingsphere://guidance complements MCP list 
methods with
     ShardingSphere domain guidance, workflow guidance, and side-effect notes.
   guidance_resource: shardingsphere://guidance
+  first_call_routes:
+  - {intent: inspect_metadata, first_action: read_resource 
shardingsphere://databases,
+    next_step: call_tool database_gateway_search_metadata or read returned 
resource.uri,
+    stop_rule: Stop after the requested detail resource is read.}
+  - {intent: validate_runtime, first_action: read_resource 
shardingsphere://runtime,
+    next_step: call_tool database_gateway_validate_runtime_database with a 
configured
+      database, stop_rule: Follow top-level next_actions when validation 
fails.}
+  - {intent: read_only_sql, first_action: 'read_resource 
shardingsphere://databases/{database}/capabilities',
+    next_step: call_tool database_gateway_execute_query, stop_rule: Stop after 
reporting
+      the result rows.}
+  - {intent: side_effect_sql, first_action: call_tool 
database_gateway_execute_update
+      execution_mode=preview, next_step: call_tool 
database_gateway_execute_update
+      execution_mode=execute, stop_rule: Execute only after preview review 
confirms
+      the intended side effect.}
+  - {intent: recover_error, first_action: follow top-level next_actions, 
next_step: fallback
+      to recovery.next_actions when top-level actions are absent, stop_rule: 
'Ask the
+      user only when no deterministic resource, completion, or tool action is 
available.'}
   metadata_rule: {first_resource: 'shardingsphere://databases', search_tool: 
database_gateway_search_metadata,
     detail_rule: Read the returned resource.uri when the list or search 
response points
       to a detail resource.}

Reply via email to