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 ccf56990d75 Clarify MCP metadata discovery before update previews 
(#40093)
ccf56990d75 is described below

commit ccf56990d75830c7804cc6063963a6f0b37c19e7
Author: Liang Zhang <[email protected]>
AuthorDate: Wed Oct 7 17:13:55 2026 +0800

    Clarify MCP metadata discovery before update previews (#40093)
    
    * Refactor DataSourcePoolCreatorTest
    
    * Skip cancellation of closed Statements during proxy cleanup
    
    * Remove obsolete Wagon HTTP options from workflows
    
    * Fix RAL E2E lifecycle exception handling
    
    * Clarify MCP metadata discovery before update previews
---
 .github/workflows/e2e-mcp.yml                      |  67 +++---
 .../content/test-manual/mcp-e2e-test/_index.cn.md  |  25 ++-
 .../content/test-manual/mcp-e2e-test/_index.en.md  |  25 ++-
 .../test/e2e/mcp/llm/LLMHttpE2EIT.java             |  35 ++-
 .../e2e/mcp/llm/config/LLME2EConfiguration.java    | 140 +++---------
 .../llm/conversation/LLMConversationRunner.java    |   2 +
 .../artifact/LLMConversationArtifactWriter.java    |  44 +---
 .../e2e/mcp/llm/fixture/LLMRuntimeSupport.java     | 239 +--------------------
 .../test/resources/docker/llm-runtime/Dockerfile   |  50 -----
 .../resources/docker/llm-runtime/build-local.sh    | 156 --------------
 .../mcp/src/test/resources/env/e2e-env.properties  |  13 +-
 11 files changed, 118 insertions(+), 678 deletions(-)

diff --git a/.github/workflows/e2e-mcp.yml b/.github/workflows/e2e-mcp.yml
index b5d67c78a07..560760c8df9 100644
--- a/.github/workflows/e2e-mcp.yml
+++ b/.github/workflows/e2e-mcp.yml
@@ -42,7 +42,7 @@ permissions:
   contents: read
 
 env:
-  MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false 
-Dmaven.wagon.http.retryHandler.class=standard 
-Dmaven.wagon.http.retryHandler.count=3
+  MAVEN_OPTS: -Dhttp.keepAlive=false
   MCP_DISTRIBUTION_IMAGE: apache/shardingsphere-mcp-e2e:local
 
 jobs:
@@ -104,40 +104,6 @@ jobs:
           docker-images: |
             ${{ env.MCP_DISTRIBUTION_IMAGE }}=mcp-distribution-image.tar
 
-  e2e-mcp-build-llm-image:
-    name: E2E - MCP Build LLM Image
-    if: ${{ needs.global-environment.outputs.GLOBAL_JOB_ENABLED == 'true' }}
-    needs: global-environment
-    runs-on: ${{ needs.global-environment.outputs.GLOBAL_RUNS_ON }}
-    timeout-minutes: 60
-    steps:
-      - uses: actions/[email protected]
-      - name: Check Docker Environment
-        run: |
-          docker version
-          docker system df
-      - name: Build MCP LLM Runtime Image
-        id: mcp-llm-runtime-image
-        shell: bash
-        run: |
-          set -euo pipefail
-          
build_script="test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh"
-          image_tag="$(sh "${build_script}" --print | sed -n 
's/^image_tag=//p')"
-          if [[ -z "${image_tag}" || "${image_tag}" == *$'\n'* ]]; then
-            echo "MCP LLM build script must print exactly one non-empty 
image_tag." >&2
-            exit 1
-          fi
-          printf 'image_tag=%s\n' "${image_tag}" >> "${GITHUB_OUTPUT}"
-          sh "${build_script}"
-      - name: Upload MCP LLM Runtime Image
-        uses: ./.github/actions/upload-e2e-artifacts
-        with:
-          artifact-name: mcp-e2e-llm-runtime-image
-          artifact-dir: ${{ runner.temp }}/mcp-e2e-llm-runtime-artifacts
-          include-maven-repository: 'false'
-          docker-images: |
-            ${{ steps.mcp-llm-runtime-image.outputs.image_tag 
}}=mcp-llm-runtime-image.tar
-
   e2e-mcp-functionality:
     name: E2E - MCP Functionality
     if: ${{ needs.global-environment.outputs.GLOBAL_JOB_ENABLED == 'true' }}
@@ -277,7 +243,7 @@ jobs:
   e2e-mcp-llm:
     name: E2E - MCP LLM - ${{ matrix.id }}
     if: ${{ needs.global-environment.outputs.GLOBAL_JOB_ENABLED == 'true' }}
-    needs: [ global-environment, e2e-mcp-build-images, e2e-mcp-build-llm-image 
]
+    needs: [ global-environment, e2e-mcp-build-images ]
     runs-on: ${{ needs.global-environment.outputs.GLOBAL_RUNS_ON }}
     timeout-minutes: 60
     strategy:
@@ -309,13 +275,28 @@ jobs:
           artifact-name: mcp-e2e-maven-repository
           artifact-dir: ${{ runner.temp }}/mcp-e2e-maven-repository
           load-docker-image: 'false'
-      - name: Download MCP LLM Runtime Image
-        uses: ./.github/actions/download-e2e-artifacts
-        with:
-          artifact-name: mcp-e2e-llm-runtime-image
-          artifact-dir: ${{ runner.temp }}/mcp-e2e-llm-runtime-artifacts
-          restore-maven-repository: 'false'
-          docker-image-archives: mcp-llm-runtime-image.tar
+      - name: Prepare MCP LLM with Docker Model Runner
+        shell: bash
+        env:
+          MODEL_RUNNER_CONTROLLER_VERSION: 
v1.2.8@sha256:5bdc2bac71c1b70453f7dec5b527031bc6d8d99b8d1b2a10a8bcca073ae257a3
+          MODEL_RUNNER_CONTROLLER_VARIANT: cpu
+        run: |
+          set -euo pipefail
+          . /etc/os-release
+          sudo install -m 0755 -d /etc/apt/keyrings
+          sudo curl --fail --silent --show-error --location 
https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
+          sudo chmod a+r /etc/apt/keyrings/docker.asc
+          echo "deb [arch=$(dpkg --print-architecture) 
signed-by=/etc/apt/keyrings/docker.asc] 
https://download.docker.com/linux/ubuntu ${UBUNTU_CODENAME:-$VERSION_CODENAME} 
stable" \
+            | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+          sudo apt-get update
+          sudo apt-get install -y 
"docker-model-plugin=1.2.6-1~ubuntu.${VERSION_ID}~${UBUNTU_CODENAME:-$VERSION_CODENAME}"
+          docker model install-runner --gpu none
+          model="$(awk -F= '$1 == "mcp.llm.model" {print $2}' 
test/e2e/mcp/src/test/resources/env/e2e-env.properties)"
+          docker model pull "${model}"
+          docker model configure --context-size 8192 "${model}" -- \
+            --jinja --reasoning-budget 0 --reasoning-format deepseek 
--chat-template-kwargs '{"enable_thinking":false}' \
+            --parallel 1 -b 256 -ub 128 --cache-ram 0 --no-cache-prompt
+          docker model run --detach "${model}"
       - name: Run MCP LLM E2E
         env:
           TESTCONTAINERS_RYUK_DISABLED: 'true'
diff --git a/docs/document/content/test-manual/mcp-e2e-test/_index.cn.md 
b/docs/document/content/test-manual/mcp-e2e-test/_index.cn.md
index 37e0f67ed36..8807e52b76c 100644
--- a/docs/document/content/test-manual/mcp-e2e-test/_index.cn.md
+++ b/docs/document/content/test-manual/mcp-e2e-test/_index.cn.md
@@ -51,23 +51,28 @@ docker build --platform "$(docker version --format 
'{{.Server.Os}}/{{.Server.Arc
 
 ## LLM Runtime
 
-MCP LLM lane 默认使用本地 Docker image 承载 OpenAI-compatible endpoint。
-构建前建议先查看 Docker 占用:
+按 [Docker 官方说明](https://docs.docker.com/ai/model-runner/get-started/)安装 Docker 
Model Runner。
+Docker Desktop 启用主机 TCP 访问:
 
 ```bash
-docker system df
+docker desktop enable model-runner --tcp 12434
 ```
 
-只校验本机架构选择,不下载模型:
+Docker Engine 安装并启动 CPU runner:
 
 ```bash
-sh test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh --dry-run
+MODEL_RUNNER_CONTROLLER_VERSION=v1.2.8@sha256:5bdc2bac71c1b70453f7dec5b527031bc6d8d99b8d1b2a10a8bcca073ae257a3
 \
+MODEL_RUNNER_CONTROLLER_VARIANT=cpu docker model install-runner --gpu none
 ```
 
-构建本地 runtime image:
+拉取、配置并启动模型:
 
 ```bash
-sh test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh
+docker model pull ai/qwen3.5:9b-q4_K_XL
+docker model configure --context-size 8192 ai/qwen3.5:9b-q4_K_XL -- \
+  --jinja --reasoning-budget 0 --reasoning-format deepseek 
--chat-template-kwargs '{"enable_thinking":false}' \
+  --parallel 1 -b 256 -ub 128 --cache-ram 0 --no-cache-prompt
+docker model run --detach ai/qwen3.5:9b-q4_K_XL
 ```
 
 ## 运行 MCP Functionality E2E
@@ -106,10 +111,10 @@ CI conformance lane 将 `modelcontextprotocol/conformance` 
固定在 commit `21a
 仅本地调试时,可以连接已经运行的 OpenAI-compatible endpoint:
 
 ```bash
-./mvnw -pl test/e2e/mcp verify -Pe2e.mcp.llm -De2e.run.type=DOCKER 
-Dit.test=LLMHttpE2EIT -Dmcp.llm.runtime-mode=external-debug 
-Dmcp.llm.base-url=http://127.0.0.1:8080/v1
+./mvnw -pl test/e2e/mcp verify -Pe2e.mcp.llm -De2e.run.type=DOCKER 
-Dit.test=LLMHttpE2EIT -Dmcp.llm.runtime-mode=external-debug 
-Dmcp.llm.base-url=http://127.0.0.1:12434/engines/v1
 ```
 
-External debug endpoint 不能作为 score-closing evidence。
+External debug endpoint 仅用于本地调试;正式 `LLMHttpE2EIT` 场景要求 Docker 和已经准备好的 Docker 
Model Runner。
 
 ## 产物
 
@@ -119,7 +124,7 @@ MCP LLM E2E artifact 写入:
 test/e2e/mcp/target/llm-e2e/
 ```
 
-每个场景都记录问题、实际答案、原始模型 response、MCP interaction trace、实时 tool definitions、runtime 
evidence 和 assertion report。Artifact 写入会脱敏 secret-shaped 值;如果发现未脱敏 secret 
pattern 或已知模型 API key,测试直接失败。
+每个场景都记录问题、实际答案、原始模型 response、MCP interaction trace、实时 tool definitions 和 
assertion report。Artifact 写入会脱敏 secret-shaped 值;如果发现未脱敏 secret pattern 或已知模型 
API key,测试直接失败。
 
 GitHub Actions 入口:
 
diff --git a/docs/document/content/test-manual/mcp-e2e-test/_index.en.md 
b/docs/document/content/test-manual/mcp-e2e-test/_index.en.md
index 6063098463f..611f48ef7e9 100644
--- a/docs/document/content/test-manual/mcp-e2e-test/_index.en.md
+++ b/docs/document/content/test-manual/mcp-e2e-test/_index.en.md
@@ -51,23 +51,28 @@ docker build --platform "$(docker version --format 
'{{.Server.Os}}/{{.Server.Arc
 
 ## LLM Runtime
 
-The MCP LLM lane uses a local Docker image to host an OpenAI-compatible 
endpoint.
-Before building, inspect Docker usage:
+Install Docker Model Runner following the [official Docker 
instructions](https://docs.docker.com/ai/model-runner/get-started/).
+For Docker Desktop, enable host TCP access:
 
 ```bash
-docker system df
+docker desktop enable model-runner --tcp 12434
 ```
 
-Check host architecture selection without downloading the model:
+For Docker Engine, install and start the CPU runner:
 
 ```bash
-sh test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh --dry-run
+MODEL_RUNNER_CONTROLLER_VERSION=v1.2.8@sha256:5bdc2bac71c1b70453f7dec5b527031bc6d8d99b8d1b2a10a8bcca073ae257a3
 \
+MODEL_RUNNER_CONTROLLER_VARIANT=cpu docker model install-runner --gpu none
 ```
 
-Build the local runtime image:
+Pull, configure, and start the model:
 
 ```bash
-sh test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh
+docker model pull ai/qwen3.5:9b-q4_K_XL
+docker model configure --context-size 8192 ai/qwen3.5:9b-q4_K_XL -- \
+  --jinja --reasoning-budget 0 --reasoning-format deepseek 
--chat-template-kwargs '{"enable_thinking":false}' \
+  --parallel 1 -b 256 -ub 128 --cache-ram 0 --no-cache-prompt
+docker model run --detach ai/qwen3.5:9b-q4_K_XL
 ```
 
 ## Run MCP Functionality E2E
@@ -106,10 +111,10 @@ The packaged server runs with its loopback HTTP 
configuration so the DNS rebindi
 For local debugging only, connect to an already running OpenAI-compatible 
endpoint:
 
 ```bash
-./mvnw -pl test/e2e/mcp verify -Pe2e.mcp.llm -De2e.run.type=DOCKER 
-Dit.test=LLMHttpE2EIT -Dmcp.llm.runtime-mode=external-debug 
-Dmcp.llm.base-url=http://127.0.0.1:8080/v1
+./mvnw -pl test/e2e/mcp verify -Pe2e.mcp.llm -De2e.run.type=DOCKER 
-Dit.test=LLMHttpE2EIT -Dmcp.llm.runtime-mode=external-debug 
-Dmcp.llm.base-url=http://127.0.0.1:12434/engines/v1
 ```
 
-External debug endpoints cannot be used as score-closing evidence.
+External debug endpoints are only for local debugging; formal `LLMHttpE2EIT` 
scenarios require Docker and a prepared Docker Model Runner.
 
 ## Artifacts
 
@@ -119,7 +124,7 @@ MCP LLM E2E artifacts are written under:
 test/e2e/mcp/target/llm-e2e/
 ```
 
-Each scenario records the question, actual answer, raw model response, MCP 
interaction trace, live tool definitions, runtime evidence, and assertion 
report. Artifact writing redacts secret-shaped values, and the test fails if an 
unredacted secret pattern or the known model API key is present.
+Each scenario records the question, actual answer, raw model response, MCP 
interaction trace, live tool definitions, and assertion report. Artifact 
writing redacts secret-shaped values, and the test fails if an unredacted 
secret pattern or the known model API key is present.
 
 GitHub Actions entry points:
 
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/LLMHttpE2EIT.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/LLMHttpE2EIT.java
index a471fa24e79..4e249ac3133 100644
--- 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/LLMHttpE2EIT.java
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/LLMHttpE2EIT.java
@@ -116,7 +116,7 @@ class LLMHttpE2EIT extends AbstractConfigBackedRuntimeE2EIT 
{
                     + 
"(?!(?:true|false|null)(?:\\s*[,}\\]]|\\s*$))[\"']?(?!<redacted>)[^\\s,\"'}]+"
                     + "|(Bearer\\s+)(?!<redacted>)[A-Za-z0-9._~+/=-]+|jdbc:");
     
-    private static LLMRuntimeSupport.ModelRuntime llmRuntime;
+    private static LLME2EConfiguration llmConfiguration;
     
     private final LLMConversationArtifactWriter artifactWriter = new 
LLMConversationArtifactWriter();
     
@@ -128,15 +128,8 @@ class LLMHttpE2EIT extends 
AbstractConfigBackedRuntimeE2EIT {
     
     @BeforeAll
     static void prepareLLMRuntime() throws InterruptedException {
-        llmRuntime = LLMRuntimeSupport.prepare(LLME2EConfiguration.load());
-    }
-    
-    @AfterAll
-    static void closeLLMRuntime() {
-        if (null != llmRuntime) {
-            llmRuntime.close();
-            llmRuntime = null;
-        }
+        llmConfiguration = LLME2EConfiguration.load();
+        LLMRuntimeSupport.prepare(llmConfiguration);
     }
     
     @AfterAll
@@ -225,16 +218,16 @@ class LLMHttpE2EIT extends 
AbstractConfigBackedRuntimeE2EIT {
     
     private void runScenario(final Scenario scenario) throws IOException {
         prepareRuntimeFixture();
-        LLMRuntimeSupport.ModelRuntime modelRuntime = getRequiredLLMRuntime();
-        assertThat("LLM E2E requires Docker-owned runtime evidence.", 
modelRuntime.getEvidence().get("scoreClosing"), is(Boolean.TRUE));
+        LLME2EConfiguration config = getRequiredLLMConfiguration();
+        assertThat("LLM E2E requires Docker Model Runner.", 
config.getRuntimeMode(), is(LLME2EConfiguration.RuntimeMode.DOCKER));
         Result actualResult = new LLMConversationRunner(
                 MAX_TURNS,
-                new LLMChatModelClient(modelRuntime.getConfiguration(), 
HttpClient.newHttpClient()),
+                new LLMChatModelClient(config, HttpClient.newHttpClient()),
                 createInteractionClient(),
-                modelRuntime.getConfiguration().getModelName()).run(scenario);
-        Path artifactDirectory = 
modelRuntime.getConfiguration().createArtifactDirectory("llm-http/" + 
scenario.id());
+                config.getModelName()).run(scenario);
+        Path artifactDirectory = config.createArtifactDirectory("llm-http/" + 
scenario.id());
         Collection<String> sensitiveValues = getArtifactSensitiveValues();
-        artifactWriter.write(artifactDirectory, actualResult, 
modelRuntime.getEvidence(), sensitiveValues);
+        artifactWriter.write(artifactDirectory, actualResult, sensitiveValues);
         assertArtifacts(artifactDirectory, sensitiveValues);
         assertTrue(actualResult.assertionReport().isSuccess(), () -> 
createFailureMessage(scenario.id(), actualResult.assertionReport(), 
artifactDirectory));
         assertFalse(actualResult.evidence().interactionTrace().isEmpty(), 
scenario.id() + " must capture MCP evidence.");
@@ -483,7 +476,7 @@ class LLMHttpE2EIT extends AbstractConfigBackedRuntimeE2EIT 
{
     
     private Collection<String> getArtifactSensitiveValues() {
         Set<String> result = new LinkedHashSet<>();
-        addArtifactSensitiveValue(result, 
getRequiredLLMRuntime().getConfiguration().getApiKey());
+        addArtifactSensitiveValue(result, 
getRequiredLLMConfiguration().getApiKey());
         for (RuntimeDatabaseConfiguration each : 
getRuntimeDatabases().values()) {
             addArtifactSensitiveValue(result, each.getJdbcUrl());
             addArtifactSensitiveValue(result, each.getPassword());
@@ -600,10 +593,10 @@ class LLMHttpE2EIT extends 
AbstractConfigBackedRuntimeE2EIT {
         return proxyRuntimeFixture;
     }
     
-    private static LLMRuntimeSupport.ModelRuntime getRequiredLLMRuntime() {
-        if (null == llmRuntime) {
-            throw new IllegalStateException("LLM runtime was not 
initialized.");
+    private static LLME2EConfiguration getRequiredLLMConfiguration() {
+        if (null == llmConfiguration) {
+            throw new IllegalStateException("LLM configuration was not 
initialized.");
         }
-        return llmRuntime;
+        return llmConfiguration;
     }
 }
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/config/LLME2EConfiguration.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/config/LLME2EConfiguration.java
index 91198a18aa2..80fe1b0b147 100644
--- 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/config/LLME2EConfiguration.java
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/config/LLME2EConfiguration.java
@@ -18,9 +18,7 @@
 package org.apache.shardingsphere.test.e2e.mcp.llm.config;
 
 import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
 import lombok.Builder;
-import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import 
org.apache.shardingsphere.test.e2e.env.runtime.EnvironmentPropertiesLoader;
@@ -38,7 +36,7 @@ import java.util.UUID;
 /**
  * LLM E2E configuration.
  */
-@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
 @Builder(toBuilder = true)
 @Getter
 public final class LLME2EConfiguration {
@@ -47,14 +45,12 @@ public final class LLME2EConfiguration {
     
     private static final DateTimeFormatter RUN_ID_FORMATTER = 
DateTimeFormatter.ofPattern("yyyyMMddHHmmss", Locale.ENGLISH);
     
-    private static final String DEFAULT_BASE_URL = "http://127.0.0.1:8080/v1";;
+    private static final String DEFAULT_BASE_URL = 
"http://127.0.0.1:12434/engines/v1";;
     
-    private static final String DEFAULT_MODEL_NAME = 
"unsloth/Qwen3.5-9B-GGUF:Q3_K_M";
+    private static final String DEFAULT_MODEL_NAME = 
"docker.io/ai/qwen3.5:9b-q4_K_XL";
     
     private static final String DEFAULT_API_KEY = "mcp-llm-score";
     
-    private static final String DEFAULT_SERVER_IMAGE = 
"apache/shardingsphere-mcp-llm-runtime:local";
-    
     private final String baseUrl;
     
     private final String modelName;
@@ -71,14 +67,6 @@ public final class LLME2EConfiguration {
     
     private final RuntimeMode runtimeMode;
     
-    private final String serverImage;
-    
-    private final String baseServerImage;
-    
-    private final String baseServerImageDigest;
-    
-    private final ModelMetadata modelMetadata;
-    
     /**
      * Load LLM E2E configuration.
      *
@@ -87,7 +75,6 @@ public final class LLME2EConfiguration {
     public static LLME2EConfiguration load() {
         Properties props = EnvironmentPropertiesLoader.loadProperties();
         RuntimeMode runtimeMode = RuntimeMode.from(readString(props, 
"mcp.llm.runtime-mode", RuntimeMode.DOCKER.getValue()));
-        ModelMetadata modelMetadata = readModelMetadata(props);
         return LLME2EConfiguration.builder()
                 .baseUrl(normalizeBaseUrl(readString(props, 
"mcp.llm.base-url", DEFAULT_BASE_URL)))
                 .modelName(readString(props, "mcp.llm.model", 
DEFAULT_MODEL_NAME))
@@ -97,13 +84,35 @@ public final class LLME2EConfiguration {
                 .artifactRoot(Paths.get(readString(props, 
"mcp.llm.artifact-root", "target/llm-e2e")))
                 .runId(readString(props, "mcp.llm.run-id", 
createDefaultRunId()))
                 .runtimeMode(runtimeMode)
-                .serverImage(readString(props, "mcp.llm.server-image", 
DEFAULT_SERVER_IMAGE))
-                .baseServerImage(readString(props, 
"mcp.llm.base-server-image", ""))
-                .baseServerImageDigest(readString(props, 
"mcp.llm.base-server-image-digest", ""))
-                .modelMetadata(modelMetadata)
                 .build();
     }
     
+    private static String readString(final Properties props, final String 
propertyName, final String defaultValue) {
+        String result = props.getProperty(propertyName);
+        return null == result || result.trim().isEmpty() ? defaultValue : 
result.trim();
+    }
+    
+    private static String normalizeBaseUrl(final String baseUrl) {
+        return baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 
1) : baseUrl;
+    }
+    
+    private static int readInteger(final Properties props, final String 
propertyName, final int defaultValue) {
+        String result = readString(props, propertyName, 
String.valueOf(defaultValue));
+        try {
+            int parsedValue = Integer.parseInt(result);
+            if (parsedValue <= 0) {
+                throw new IllegalStateException(String.format("MCP LLM E2E 
property `%s` must be a positive integer, but was `%s`.", propertyName, 
result));
+            }
+            return parsedValue;
+        } catch (final NumberFormatException ex) {
+            throw new IllegalStateException(String.format("MCP LLM E2E 
property `%s` must be a positive integer, but was `%s`.", propertyName, 
result), ex);
+        }
+    }
+    
+    private static String createDefaultRunId() {
+        return RUN_ID_FORMATTER.format(LocalDateTime.now()) + "-" + 
UUID.randomUUID().toString().substring(0, 8);
+    }
+    
     /**
      * Create artifact directory.
      *
@@ -117,17 +126,6 @@ public final class LLME2EConfiguration {
         return result;
     }
     
-    /**
-     * Create a copy with another model endpoint and API key.
-     *
-     * @param baseUrl model endpoint base URL
-     * @param apiKey API key
-     * @return copied configuration
-     */
-    public LLME2EConfiguration withModelEndpoint(final String baseUrl, final 
String apiKey) {
-        return 
toBuilder().baseUrl(normalizeBaseUrl(baseUrl)).apiKey(apiKey).build();
-    }
-    
     /**
      * Create a copy with shorter readiness probe timeouts.
      *
@@ -157,86 +155,6 @@ public final class LLME2EConfiguration {
         return baseUrl + "/models";
     }
     
-    /**
-     * Get model SHA-256 checksum.
-     *
-     * @return model SHA-256 checksum
-     */
-    public String getModelSha256() {
-        return modelMetadata.getSha256();
-    }
-    
-    private static String readString(final Properties props, final String 
propertyName, final String defaultValue) {
-        String result = props.getProperty(propertyName);
-        return null == result || result.trim().isEmpty() ? defaultValue : 
result.trim();
-    }
-    
-    private static String readRequiredString(final Properties props, final 
String propertyName) {
-        String result = readString(props, propertyName, "");
-        if (result.isEmpty()) {
-            throw new IllegalStateException(String.format("MCP LLM E2E 
property `%s` is required.", propertyName));
-        }
-        return result;
-    }
-    
-    private static int readInteger(final Properties props, final String 
propertyName, final int defaultValue) {
-        String result = readString(props, propertyName, 
String.valueOf(defaultValue));
-        try {
-            int parsedValue = Integer.parseInt(result);
-            if (parsedValue <= 0) {
-                throw new IllegalStateException(String.format("MCP LLM E2E 
property `%s` must be a positive integer, but was `%s`.", propertyName, 
result));
-            }
-            return parsedValue;
-        } catch (final NumberFormatException ex) {
-            throw new IllegalStateException(String.format("MCP LLM E2E 
property `%s` must be a positive integer, but was `%s`.", propertyName, 
result), ex);
-        }
-    }
-    
-    private static ModelMetadata readModelMetadata(final Properties props) {
-        return new ModelMetadata(
-                readRequiredString(props, "mcp.llm.model-repository"),
-                readRequiredString(props, "mcp.llm.model-file-name"),
-                readRequiredString(props, "mcp.llm.model-quantization"),
-                readRequiredString(props, "mcp.llm.model-revision"),
-                readRequiredString(props, "mcp.llm.model-sha256"));
-    }
-    
-    private static String normalizeBaseUrl(final String baseUrl) {
-        return baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 
1) : baseUrl;
-    }
-    
-    private static String createDefaultRunId() {
-        return RUN_ID_FORMATTER.format(LocalDateTime.now()) + "-" + 
UUID.randomUUID().toString().substring(0, 8);
-    }
-    
-    /**
-     * LLM model metadata.
-     */
-    @RequiredArgsConstructor
-    @Getter
-    @EqualsAndHashCode
-    public static final class ModelMetadata {
-        
-        private final String repository;
-        
-        private final String fileName;
-        
-        private final String quantization;
-        
-        private final String revision;
-        
-        private final String sha256;
-        
-        /**
-         * Get model path inside the runtime container.
-         *
-         * @return model path
-         */
-        public String getContainerPath() {
-            return "/models/" + fileName;
-        }
-    }
-    
     /**
      * LLM runtime mode.
      */
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMConversationRunner.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMConversationRunner.java
index 7e01542c358..c04317a2e60 100644
--- 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMConversationRunner.java
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/LLMConversationRunner.java
@@ -51,6 +51,8 @@ public final class LLMConversationRunner {
     
     private static final String SYSTEM_PROMPT = """
             You are evaluating a live ShardingSphere MCP server. Use the 
available MCP functions to inspect current state and complete the user's task.
+            Before constructing an UPDATE statement for preview, retrieve the 
target table's complete column list from MCP.
+            Use the returned column names for both assignments and row 
selection.
             Choose tools from their advertised names, descriptions, and input 
schemas. Pass resource URIs only to mcp_read_resource, never as SQL.
             Never guess or stop before retrieving the requested evidence. When 
target metadata is explicitly marked as already verified, use the requested
             planning response as evidence instead of repeating metadata 
discovery. Preview side effects without executing them. Use function calls for
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/artifact/LLMConversationArtifactWriter.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/artifact/LLMConversationArtifactWriter.java
index ffec90296c7..cd4e820a14c 100644
--- 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/artifact/LLMConversationArtifactWriter.java
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/conversation/artifact/LLMConversationArtifactWriter.java
@@ -25,7 +25,6 @@ import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Collection;
-import java.util.List;
 import java.util.Map;
 
 /**
@@ -33,24 +32,16 @@ import java.util.Map;
  */
 public final class LLMConversationArtifactWriter {
     
-    private static final List<String> REQUIRED_SCORE_EVIDENCE_KEYS = List.of(
-            "runtimeMode", "dockerOwned", "provider", "serverRuntime", 
"serverImage", "serverImageId", "baseServerImage", "baseServerImageDigest",
-            "modelRepository", "modelReference", "servedModelId",
-            "modelQuantization", "modelRevision", "modelFileName", 
"modelSha256", "modelPackaging", "contextWindowTokens", "baseUrlOwnedByTest");
-    
     /**
      * Write one conversation result.
      *
      * @param artifactDirectory artifact directory
      * @param conversationResult conversation result
-     * @param runtimeEvidence runtime evidence
      * @param sensitiveValues concrete sensitive values
      * @throws IOException IO exception
      */
-    public void write(final Path artifactDirectory, final Result 
conversationResult, final Map<String, Object> runtimeEvidence,
-                      final Collection<String> sensitiveValues) throws 
IOException {
-        validateRuntimeEvidence(runtimeEvidence);
-        writeContent(artifactDirectory.resolve("run-context.json"), 
JsonEngine.marshal(createRunContext(conversationResult, runtimeEvidence)), 
sensitiveValues);
+    public void write(final Path artifactDirectory, final Result 
conversationResult, final Collection<String> sensitiveValues) throws 
IOException {
+        writeContent(artifactDirectory.resolve("run-context.json"), 
JsonEngine.marshal(createRunContext(conversationResult)), sensitiveValues);
         writeContent(artifactDirectory.resolve("system-prompt.md"), 
conversationResult.systemPrompt(), sensitiveValues);
         writeContent(artifactDirectory.resolve("question.txt"), 
conversationResult.scenario().question(), sensitiveValues);
         writeContent(artifactDirectory.resolve("answer.txt"), 
conversationResult.actualAnswer(), sensitiveValues);
@@ -61,35 +52,16 @@ public final class LLMConversationArtifactWriter {
         writeContent(artifactDirectory.resolve("assertion-report.json"), 
JsonEngine.marshal(conversationResult.assertionReport()), sensitiveValues);
     }
     
-    private Map<String, Object> createRunContext(final Result 
conversationResult, final Map<String, Object> runtimeEvidence) {
+    private void writeContent(final Path file, final String content, final 
Collection<String> sensitiveValues) throws IOException {
+        Files.createDirectories(file.getParent());
+        Files.writeString(file, MCPArtifactUtils.redact(content, 
sensitiveValues));
+    }
+    
+    private Map<String, Object> createRunContext(final Result 
conversationResult) {
         return Map.of(
                 "scenarioId", conversationResult.scenario().id(),
                 "modelProvider", conversationResult.modelProvider(),
                 "modelName", conversationResult.modelName(),
-                "runtime", runtimeEvidence,
                 "failureType", 
conversationResult.assertionReport().getFailureType());
     }
-    
-    private void validateRuntimeEvidence(final Map<String, Object> 
runtimeEvidence) {
-        if (!Boolean.TRUE.equals(runtimeEvidence.get("scoreClosing"))) {
-            return;
-        }
-        for (String each : REQUIRED_SCORE_EVIDENCE_KEYS) {
-            if (isMissingEvidenceValue(runtimeEvidence.get(each))) {
-                throw new IllegalStateException(String.format("Missing 
score-closing LLM runtime evidence field `%s`.", each));
-            }
-        }
-        if (!Boolean.TRUE.equals(runtimeEvidence.get("dockerOwned")) || 
!Boolean.TRUE.equals(runtimeEvidence.get("baseUrlOwnedByTest"))) {
-            throw new IllegalStateException("Score-closing LLM runtime 
evidence must be Docker-owned and test-owned.");
-        }
-    }
-    
-    private boolean isMissingEvidenceValue(final Object value) {
-        return null == value || value instanceof String && ((String) 
value).isBlank();
-    }
-    
-    private void writeContent(final Path file, final String content, final 
Collection<String> sensitiveValues) throws IOException {
-        Files.createDirectories(file.getParent());
-        Files.writeString(file, MCPArtifactUtils.redact(content, 
sensitiveValues));
-    }
 }
diff --git 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/fixture/LLMRuntimeSupport.java
 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/fixture/LLMRuntimeSupport.java
index 6b892e9c230..805b3942335 100644
--- 
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/fixture/LLMRuntimeSupport.java
+++ 
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/fixture/LLMRuntimeSupport.java
@@ -17,105 +17,37 @@
 
 package org.apache.shardingsphere.test.e2e.mcp.llm.fixture;
 
-import com.github.dockerjava.api.command.InspectImageResponse;
-import com.github.dockerjava.api.exception.NotFoundException;
 import lombok.AccessLevel;
-import lombok.Getter;
 import lombok.NoArgsConstructor;
-import lombok.RequiredArgsConstructor;
 import org.apache.shardingsphere.test.e2e.mcp.llm.config.LLME2EConfiguration;
 import 
org.apache.shardingsphere.test.e2e.mcp.llm.config.LLME2EConfiguration.RuntimeMode;
 import 
org.apache.shardingsphere.test.e2e.mcp.llm.conversation.client.LLMChatModelClient;
 import 
org.apache.shardingsphere.test.e2e.mcp.support.runtime.DockerRuntimeTestSupport;
-import org.testcontainers.DockerClientFactory;
-import org.testcontainers.containers.ContainerLaunchException;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.containers.wait.strategy.Wait;
-import org.testcontainers.utility.DockerImageName;
 
 import java.net.http.HttpClient;
-import java.time.Duration;
-import java.util.LinkedHashMap;
-import java.util.Map;
 
 /**
- * Docker-owned LLM runtime support for MCP LLM E2E.
+ * Docker Model Runner readiness support for MCP LLM E2E.
  */
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public final class LLMRuntimeSupport {
     
-    private static final int CONTEXT_WINDOW_TOKENS = 8192;
-    
-    private static final int SERVER_PORT = 8080;
-    
-    private static final String LABEL_PREFIX = 
"org.apache.shardingsphere.mcp.llm.";
-    
-    private static final String RUNTIME_LABEL = LABEL_PREFIX + "runtime";
-    
-    private static final String BASE_SERVER_IMAGE_LABEL = LABEL_PREFIX + 
"base-server-image";
-    
-    private static final String BASE_SERVER_IMAGE_DIGEST_LABEL = LABEL_PREFIX 
+ "base-server-image-digest";
-    
-    private static final String MODEL_REPOSITORY_LABEL = LABEL_PREFIX + 
"model-repository";
-    
-    private static final String MODEL_QUANTIZATION_LABEL = LABEL_PREFIX + 
"model-quantization";
-    
-    private static final String MODEL_REFERENCE_LABEL = LABEL_PREFIX + 
"model-reference";
-    
-    private static final String MODEL_REVISION_LABEL = LABEL_PREFIX + 
"model-revision";
-    
-    private static final String MODEL_FILE_NAME_LABEL = LABEL_PREFIX + 
"model-file-name";
-    
-    private static final String MODEL_SHA256_LABEL = LABEL_PREFIX + 
"model-sha256";
-    
-    private static ModelRuntime sharedContainerRuntime;
-    
     /**
-     * Prepare the required local LLM runtime.
+     * Check the LLM runtime prepared by the Docker Model Runner CLI.
      *
      * @param config LLM E2E configuration
-     * @return prepared LLM runtime
-     * @throws IllegalStateException when the runtime cannot be prepared
+     * @throws IllegalStateException when the runtime is unavailable
      * @throws InterruptedException interrupted exception
      */
-    public static synchronized ModelRuntime prepare(final LLME2EConfiguration 
config) throws InterruptedException {
+    public static void prepare(final LLME2EConfiguration config) throws 
InterruptedException {
         if (RuntimeMode.EXTERNAL_DEBUG == config.getRuntimeMode()) {
-            return prepareExternalDebugRuntime(config);
-        }
-        if (null != sharedContainerRuntime && 
sharedContainerRuntime.isReusable(config)) {
-            return sharedContainerRuntime;
-        }
-        stopSharedRuntime();
-        requireDockerAvailable();
-        ScoreImage scoreImage = requireScoreImageAvailable(config);
-        GenericContainer<?> container = createContainer(config);
-        try {
-            container.start();
-        } catch (final ContainerLaunchException ex) {
-            String containerLogs;
-            try {
-                containerLogs = container.getLogs();
-            } catch (final IllegalStateException logException) {
-                containerLogs = "<unavailable: " + logException.getMessage() + 
">";
+            if (!isModelReady(config)) {
+                throw new IllegalStateException("MCP LLM external-debug mode 
requires a ready OpenAI-compatible endpoint.");
             }
-            containerLogs = containerLogs.isBlank() ? "<empty>" : 
containerLogs;
-            throw new IllegalStateException(String.format(
-                    "MCP LLM Docker runtime container failed to start. 
serverImage=`%s`, baseServerImage=`%s`, baseServerImageDigest=`%s`, 
javaOs=`%s`, javaArch=`%s`, containerLogs=%s",
-                    config.getServerImage(), config.getBaseServerImage(), 
config.getBaseServerImageDigest(), System.getProperty("os.name"), 
System.getProperty("os.arch"),
-                    containerLogs), ex);
+            return;
         }
-        LLME2EConfiguration actualConfig = 
createDockerRuntimeConfiguration(config, container);
-        new LLMChatModelClient(actualConfig, 
HttpClient.newHttpClient()).waitUntilReady();
-        sharedContainerRuntime = ModelRuntime.container(actualConfig, 
container, scoreImage);
-        registerShutdownHook(sharedContainerRuntime);
-        return sharedContainerRuntime;
-    }
-    
-    private static ModelRuntime prepareExternalDebugRuntime(final 
LLME2EConfiguration config) throws InterruptedException {
-        if (!isModelReady(config)) {
-            throw new IllegalStateException("MCP LLM external-debug mode 
requires a ready OpenAI-compatible endpoint.");
-        }
-        return ModelRuntime.externalDebug(config);
+        DockerRuntimeTestSupport.requireAvailable("Docker is required to run 
Docker Model Runner for MCP LLM E2E.");
+        new LLMChatModelClient(config, 
HttpClient.newHttpClient()).waitUntilReady();
     }
     
     private static boolean isModelReady(final LLME2EConfiguration config) 
throws InterruptedException {
@@ -126,157 +58,4 @@ public final class LLMRuntimeSupport {
             return false;
         }
     }
-    
-    private static void stopSharedRuntime() {
-        if (null != sharedContainerRuntime) {
-            sharedContainerRuntime.stop();
-            sharedContainerRuntime = null;
-        }
-    }
-    
-    private static void requireDockerAvailable() {
-        DockerRuntimeTestSupport.requireAvailable("Docker is required to start 
the prepackaged llama.cpp server for MCP LLM E2E.");
-    }
-    
-    private static ScoreImage requireScoreImageAvailable(final 
LLME2EConfiguration config) {
-        try {
-            InspectImageResponse image = 
DockerClientFactory.instance().client().inspectImageCmd(config.getServerImage()).exec();
-            Map<String, String> labels = null == image.getConfig() ? Map.of() 
: image.getConfig().getLabels();
-            labels = null == labels ? Map.of() : labels;
-            validateImageLabel(labels, RUNTIME_LABEL, "llama.cpp");
-            validateImageLabel(labels, BASE_SERVER_IMAGE_LABEL, 
config.getBaseServerImage());
-            validateImageLabel(labels, BASE_SERVER_IMAGE_DIGEST_LABEL, 
config.getBaseServerImageDigest());
-            validateImageLabel(labels, MODEL_REPOSITORY_LABEL, 
config.getModelMetadata().getRepository());
-            validateImageLabel(labels, MODEL_QUANTIZATION_LABEL, 
config.getModelMetadata().getQuantization());
-            validateImageLabel(labels, MODEL_REFERENCE_LABEL, 
config.getModelName());
-            validateImageLabel(labels, MODEL_REVISION_LABEL, 
config.getModelMetadata().getRevision());
-            validateImageLabel(labels, MODEL_FILE_NAME_LABEL, 
config.getModelMetadata().getFileName());
-            validateImageLabel(labels, MODEL_SHA256_LABEL, 
config.getModelSha256());
-            if (null == image.getId() || image.getId().isBlank()) {
-                throw new IllegalStateException(String.format("MCP LLM Docker 
score image `%s` has no immutable image ID.", config.getServerImage()));
-            }
-            return new ScoreImage(image.getId(), labels);
-        } catch (final NotFoundException ex) {
-            throw new IllegalStateException(String.format(
-                    "MCP LLM Docker score mode requires prebuilt local image 
`%s`. Build it before running Maven LLM tests.", config.getServerImage()), ex);
-        }
-    }
-    
-    private static void validateImageLabel(final Map<String, String> labels, 
final String labelName, final String expectedValue) {
-        String actualValue = labels.get(labelName);
-        if (expectedValue.isBlank() || !expectedValue.equals(actualValue)) {
-            throw new IllegalStateException(String.format(
-                    "MCP LLM Docker score image label `%s` must match 
non-empty configuration. expected=`%s`, actual=`%s`.", labelName, 
expectedValue, actualValue));
-        }
-    }
-    
-    private static GenericContainer<?> createContainer(final 
LLME2EConfiguration config) {
-        return new 
GenericContainer<>(DockerImageName.parse(config.getServerImage()))
-                .withImagePullPolicy(imageName -> false)
-                .withExposedPorts(SERVER_PORT)
-                .withCommand("--host", "0.0.0.0", "--port", 
String.valueOf(SERVER_PORT), "-m", 
config.getModelMetadata().getContainerPath(), "--alias", config.getModelName(),
-                        "--jinja", "--reasoning", "auto", 
"--reasoning-format", "deepseek", "--reasoning-budget", "0", 
"--chat-template-kwargs", "{\"enable_thinking\":false}",
-                        "--api-key", config.getApiKey(), "--no-ui", 
"--parallel", "1", "-c", String.valueOf(CONTEXT_WINDOW_TOKENS),
-                        "-b", "256", "-ub", "128", "--cache-ram", "0", 
"--no-cache-prompt")
-                .waitingFor(Wait.forListeningPort())
-                .withStartupTimeout(Duration.ofMinutes(5));
-    }
-    
-    private static LLME2EConfiguration createDockerRuntimeConfiguration(final 
LLME2EConfiguration config, final GenericContainer<?> container) {
-        return config.withModelEndpoint(String.format("http://%s:%d/v1";, 
container.getHost(), container.getMappedPort(SERVER_PORT)), config.getApiKey());
-    }
-    
-    private static void registerShutdownHook(final ModelRuntime runtime) {
-        Runtime.getRuntime().addShutdownHook(new Thread(runtime::stop, 
"mcp-llm-runtime-shutdown"));
-    }
-    
-    private record ScoreImage(String id, Map<String, String> labels) {
-    }
-    
-    /**
-     * Prepared model runtime.
-     */
-    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
-    public static final class ModelRuntime implements AutoCloseable {
-        
-        @Getter
-        private final LLME2EConfiguration configuration;
-        
-        private final GenericContainer<?> container;
-        
-        @Getter
-        private final Map<String, Object> evidence;
-        
-        private static ModelRuntime externalDebug(final LLME2EConfiguration 
config) {
-            return new ModelRuntime(config, null, 
createExternalDebugEvidence(config));
-        }
-        
-        private static ModelRuntime container(final LLME2EConfiguration 
config, final GenericContainer<?> container, final ScoreImage scoreImage) {
-            return new ModelRuntime(config, container, 
createScoreClosingEvidence(config, scoreImage));
-        }
-        
-        private static Map<String, Object> createExternalDebugEvidence(final 
LLME2EConfiguration config) {
-            Map<String, Object> result = new LinkedHashMap<>(8, 1F);
-            result.put("runtimeMode", config.getRuntimeMode().getValue());
-            result.put("dockerOwned", false);
-            result.put("provider", LLME2EConfiguration.MODEL_PROVIDER);
-            result.put("serverRuntime", "external-openai-compatible");
-            result.put("modelReference", config.getModelName());
-            result.put("servedModelId", config.getModelName());
-            result.put("modelPackaging", "external");
-            result.put("baseUrlOwnedByTest", false);
-            result.put("scoreClosing", false);
-            return result;
-        }
-        
-        private static Map<String, Object> createScoreClosingEvidence(final 
LLME2EConfiguration config, final ScoreImage scoreImage) {
-            Map<String, String> labels = scoreImage.labels();
-            Map<String, Object> result = new LinkedHashMap<>(20, 1F);
-            result.put("runtimeMode", config.getRuntimeMode().getValue());
-            result.put("dockerOwned", true);
-            result.put("provider", LLME2EConfiguration.MODEL_PROVIDER);
-            result.put("serverRuntime", labels.get(RUNTIME_LABEL));
-            result.put("serverImage", config.getServerImage());
-            result.put("serverImageId", scoreImage.id());
-            result.put("baseServerImage", labels.get(BASE_SERVER_IMAGE_LABEL));
-            result.put("baseServerImageDigest", 
labels.get(BASE_SERVER_IMAGE_DIGEST_LABEL));
-            result.put("modelRepository", labels.get(MODEL_REPOSITORY_LABEL));
-            result.put("modelReference", labels.get(MODEL_REFERENCE_LABEL));
-            result.put("servedModelId", config.getModelName());
-            result.put("modelQuantization", 
labels.get(MODEL_QUANTIZATION_LABEL));
-            result.put("modelRevision", labels.get(MODEL_REVISION_LABEL));
-            result.put("modelFileName", labels.get(MODEL_FILE_NAME_LABEL));
-            result.put("modelSha256", labels.get(MODEL_SHA256_LABEL));
-            result.put("modelPackaging", "prepackaged");
-            result.put("contextWindowTokens", CONTEXT_WINDOW_TOKENS);
-            result.put("baseUrlOwnedByTest", true);
-            result.put("scoreClosing", true);
-            return result;
-        }
-        
-        @Override
-        public void close() {
-            if (this == sharedContainerRuntime) {
-                return;
-            }
-            stop();
-        }
-        
-        private boolean isReusable(final LLME2EConfiguration config) {
-            return null != container && container.isRunning()
-                    && configuration.getRuntimeMode() == 
config.getRuntimeMode()
-                    && 
configuration.getModelName().equals(config.getModelName())
-                    && configuration.getApiKey().equals(config.getApiKey())
-                    && 
configuration.getServerImage().equals(config.getServerImage())
-                    && 
configuration.getBaseServerImage().equals(config.getBaseServerImage())
-                    && 
configuration.getBaseServerImageDigest().equals(config.getBaseServerImageDigest())
-                    && 
configuration.getModelMetadata().equals(config.getModelMetadata());
-        }
-        
-        private void stop() {
-            if (null != container) {
-                container.stop();
-            }
-        }
-    }
 }
diff --git a/test/e2e/mcp/src/test/resources/docker/llm-runtime/Dockerfile 
b/test/e2e/mcp/src/test/resources/docker/llm-runtime/Dockerfile
deleted file mode 100644
index b915b900433..00000000000
--- a/test/e2e/mcp/src/test/resources/docker/llm-runtime/Dockerfile
+++ /dev/null
@@ -1,50 +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.
-#
-
-ARG BASE_IMAGE
-FROM ${BASE_IMAGE}
-ARG BASE_SERVER_IMAGE
-ARG BASE_SERVER_IMAGE_DIGEST
-ARG MODEL_REPOSITORY
-ARG MODEL_QUANTIZATION
-ARG MODEL_REFERENCE
-ARG MODEL_REVISION
-ARG MODEL_FILE_NAME
-ARG MODEL_SHA256
-
-LABEL org.opencontainers.image.title="Apache ShardingSphere MCP LLM E2E 
Runtime"
-LABEL org.opencontainers.image.description="Prepackaged model server for MCP 
LLM E2E score evidence"
-LABEL org.opencontainers.image.licenses="Apache-2.0"
-LABEL org.apache.shardingsphere.mcp.llm.runtime="llama.cpp"
-LABEL 
org.apache.shardingsphere.mcp.llm.base-server-image="${BASE_SERVER_IMAGE}"
-LABEL 
org.apache.shardingsphere.mcp.llm.base-server-image-digest="${BASE_SERVER_IMAGE_DIGEST}"
-LABEL org.apache.shardingsphere.mcp.llm.model-repository="${MODEL_REPOSITORY}"
-LABEL 
org.apache.shardingsphere.mcp.llm.model-quantization="${MODEL_QUANTIZATION}"
-LABEL org.apache.shardingsphere.mcp.llm.model-reference="${MODEL_REFERENCE}"
-LABEL org.apache.shardingsphere.mcp.llm.model-revision="${MODEL_REVISION}"
-LABEL org.apache.shardingsphere.mcp.llm.model-file-name="${MODEL_FILE_NAME}"
-LABEL org.apache.shardingsphere.mcp.llm.model-sha256="${MODEL_SHA256}"
-
-ENV LD_LIBRARY_PATH=/app
-
-RUN set -eux; \
-    mkdir -p /models; \
-    curl --fail --location --retry 3 --output "/models/${MODEL_FILE_NAME}" \
-        
"https://huggingface.co/${MODEL_REPOSITORY}/resolve/${MODEL_REVISION}/${MODEL_FILE_NAME}";;
 \
-    printf '%s  %s\n' "${MODEL_SHA256}" "/models/${MODEL_FILE_NAME}" | 
sha256sum -c -
-
-EXPOSE 8080
diff --git a/test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh 
b/test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh
deleted file mode 100644
index 5ecc41baafc..00000000000
--- a/test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/bin/sh
-#
-# 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.
-#
-
-set -eu
-
-print_usage() {
-  echo "Usage: sh $0 [--dry-run|--print]"
-}
-
-if [ "$#" -gt 1 ]; then
-  echo "Only one argument is supported." >&2
-  print_usage >&2
-  exit 1
-fi
-
-MODE="${1:-build}"
-
-case "${MODE}" in
-  build | --dry-run | --print)
-    ;;
-  --help)
-    print_usage
-    exit 0
-    ;;
-  *)
-    echo "Unsupported argument: $1" >&2
-    print_usage >&2
-    exit 1
-    ;;
-esac
-
-SCRIPT_DIR="$(CDPATH= cd "$(dirname "$0")" && pwd -P)"
-DOCKERFILE_PATH="${SCRIPT_DIR}/Dockerfile"
-ENV_FILE="${SCRIPT_DIR}/../../env/e2e-env.properties"
-
-if [ ! -f "${ENV_FILE}" ]; then
-  echo "MCP LLM E2E environment properties file is required: ${ENV_FILE}" >&2
-  exit 1
-fi
-
-read_property() {
-  awk -F= -v key="$1" '$1 == key {sub(/^[^=]*=/, ""); print; found = 1; exit} 
END {if (!found) exit 1}' "${ENV_FILE}"
-}
-
-read_required_property() {
-  PROPERTY_VALUE="$(read_property "$1" || true)"
-  if [ -z "${PROPERTY_VALUE}" ]; then
-    echo "MCP LLM E2E property is required: $1" >&2
-    exit 1
-  fi
-  echo "${PROPERTY_VALUE}"
-}
-
-read_optional_property() {
-  read_property "$1" || true
-}
-
-normalize_arch() {
-  case "$1" in
-    x86_64)
-      echo "amd64"
-      ;;
-    aarch64)
-      echo "arm64"
-      ;;
-    *)
-      echo "$1"
-      ;;
-  esac
-}
-
-detect_target_platform() {
-  DOCKER_PLATFORM="$(docker version --format '{{.Server.Os}}/{{.Server.Arch}}' 
2>/dev/null || true)"
-  if [ -n "${DOCKER_PLATFORM}" ]; then
-    echo "${DOCKER_PLATFORM}"
-    return
-  fi
-  echo "$(uname -s | tr '[:upper:]' '[:lower:]')/$(normalize_arch "$(uname 
-m)")"
-}
-
-IMAGE_TAG="${MCP_LLM_SERVER_IMAGE:-$(read_required_property 
"mcp.llm.server-image")}"
-TARGET_PLATFORM="${MCP_LLM_TARGET_PLATFORM:-$(read_optional_property 
"mcp.llm.target-platform")}"
-if [ -z "${TARGET_PLATFORM}" ]; then
-  TARGET_PLATFORM="$(detect_target_platform)"
-fi
-BASE_SERVER_IMAGE="${MCP_LLM_BASE_SERVER_IMAGE:-$(read_required_property 
"mcp.llm.base-server-image")}"
-BASE_SERVER_IMAGE_DIGEST="${MCP_LLM_BASE_SERVER_IMAGE_DIGEST:-$(read_optional_property
 "mcp.llm.base-server-image-digest")}"
-BASE_IMAGE="${BASE_SERVER_IMAGE}"
-MODEL_REPOSITORY="${MCP_LLM_MODEL_REPOSITORY:-$(read_required_property 
"mcp.llm.model-repository")}"
-MODEL_QUANTIZATION="${MCP_LLM_MODEL_QUANTIZATION:-$(read_required_property 
"mcp.llm.model-quantization")}"
-MODEL_REFERENCE="${MCP_LLM_MODEL:-$(read_required_property "mcp.llm.model")}"
-MODEL_REVISION="${MCP_LLM_MODEL_REVISION:-$(read_required_property 
"mcp.llm.model-revision")}"
-MODEL_FILE_NAME="${MCP_LLM_MODEL_FILE_NAME:-$(read_required_property 
"mcp.llm.model-file-name")}"
-MODEL_SHA256="${MCP_LLM_MODEL_SHA256:-$(read_required_property 
"mcp.llm.model-sha256")}"
-
-case "${BASE_IMAGE}" in
-  *@sha256:*)
-    ;;
-  *)
-    if [ -n "${BASE_SERVER_IMAGE_DIGEST}" ]; then
-      BASE_IMAGE="${BASE_IMAGE}@${BASE_SERVER_IMAGE_DIGEST}"
-    fi
-    ;;
-esac
-
-if [ "--dry-run" = "${MODE}" ] || [ "--print" = "${MODE}" ]; then
-  echo "target_platform=${TARGET_PLATFORM}"
-  echo "base_image=${BASE_IMAGE}"
-  echo "base_server_image=${BASE_SERVER_IMAGE}"
-  echo "base_server_image_digest=${BASE_SERVER_IMAGE_DIGEST}"
-  echo "model_repository=${MODEL_REPOSITORY}"
-  echo "model_quantization=${MODEL_QUANTIZATION}"
-  echo "model_reference=${MODEL_REFERENCE}"
-  echo "model_revision=${MODEL_REVISION}"
-  echo "model_file_name=${MODEL_FILE_NAME}"
-  echo "model_sha256=${MODEL_SHA256}"
-  echo "image_tag=${IMAGE_TAG}"
-  echo "dockerfile=${DOCKERFILE_PATH}"
-  echo "context=${SCRIPT_DIR}"
-  exit 0
-fi
-
-if ! command -v docker >/dev/null 2>&1; then
-  echo "Docker is required to build the MCP LLM runtime image." >&2
-  exit 1
-fi
-
-docker build \
-  --platform "${TARGET_PLATFORM}" \
-  --build-arg "BASE_IMAGE=${BASE_IMAGE}" \
-  --build-arg "BASE_SERVER_IMAGE=${BASE_SERVER_IMAGE}" \
-  --build-arg "BASE_SERVER_IMAGE_DIGEST=${BASE_SERVER_IMAGE_DIGEST}" \
-  --build-arg "MODEL_REPOSITORY=${MODEL_REPOSITORY}" \
-  --build-arg "MODEL_QUANTIZATION=${MODEL_QUANTIZATION}" \
-  --build-arg "MODEL_REFERENCE=${MODEL_REFERENCE}" \
-  --build-arg "MODEL_REVISION=${MODEL_REVISION}" \
-  --build-arg "MODEL_FILE_NAME=${MODEL_FILE_NAME}" \
-  --build-arg "MODEL_SHA256=${MODEL_SHA256}" \
-  -t "${IMAGE_TAG}" \
-  -f "${DOCKERFILE_PATH}" \
-  "${SCRIPT_DIR}"
diff --git a/test/e2e/mcp/src/test/resources/env/e2e-env.properties 
b/test/e2e/mcp/src/test/resources/env/e2e-env.properties
index 7b45fbc89ee..6e45dde3588 100644
--- a/test/e2e/mcp/src/test/resources/env/e2e-env.properties
+++ b/test/e2e/mcp/src/test/resources/env/e2e-env.properties
@@ -27,19 +27,10 @@ mcp.e2e.mysql.ready-timeout-seconds=90
 mcp.distribution.home=
 
 mcp.llm.runtime-mode=docker
-mcp.llm.base-url=http://127.0.0.1:8080/v1
-mcp.llm.model=unsloth/Qwen3.5-9B-GGUF:Q3_K_M
+mcp.llm.base-url=http://127.0.0.1:12434/engines/v1
+mcp.llm.model=docker.io/ai/qwen3.5:9b-q4_K_XL
 mcp.llm.api-key=mcp-llm-score
 mcp.llm.ready-timeout-seconds=600
 mcp.llm.request-timeout-seconds=600
 mcp.llm.artifact-root=target/llm-e2e
 mcp.llm.run-id=
-mcp.llm.server-image=apache/shardingsphere-mcp-llm-runtime:local
-mcp.llm.target-platform=
-mcp.llm.base-server-image=ghcr.io/ggml-org/llama.cpp:server-b9191
-mcp.llm.base-server-image-digest=sha256:d03fd3f78c85352b4b35bd9aa9611a932b9bca2656a6bf4804e100e2afc63605
-mcp.llm.model-repository=unsloth/Qwen3.5-9B-GGUF
-mcp.llm.model-quantization=Q3_K_M
-mcp.llm.model-revision=3885219b6810b007914f3a7950a8d1b469d598a5
-mcp.llm.model-file-name=Qwen3.5-9B-Q3_K_M.gguf
-mcp.llm.model-sha256=8fed90306e4f019e2bf35f3766470b7bc59ea1a9dae00f5ceb20b43cb5514393

Reply via email to