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 2061ecba9a1 Consolidate E2E workflows and stabilize LLM tests (#38777)
2061ecba9a1 is described below
commit 2061ecba9a170fb2a64a115124c6ef6b0553be2d
Author: Liang Zhang <[email protected]>
AuthorDate: Tue Jun 2 20:51:53 2026 +0800
Consolidate E2E workflows and stabilize LLM tests (#38777)
- Add one MCP E2E workflow for full MySQL runtime validation
- Keep JDK 21 subchain CI focused on MCP unit tests
- Rename MCP build workflow to release validation
- Remove obsolete standalone LLM E2E workflow entry points
- Update release and MCP E2E documentation
- Stabilize LLM usability scoring and recovery scenarios
---
.github/workflows/jdk21-subchain-ci.yml | 149 ++-------------------
.../{mcp-llm-usability-e2e.yml => mcp-e2e.yml} | 44 +++---
.github/workflows/mcp-llm-e2e.yml | 107 ---------------
.../workflows/{mcp-build.yml => mcp-release.yml} | 12 +-
.../content/involved/release/shardingsphere.cn.md | 4 +-
.../content/involved/release/shardingsphere.en.md | 4 +-
.../content/test-manual/mcp-e2e-test/_index.cn.md | 28 ++--
.../content/test-manual/mcp-e2e-test/_index.en.md | 28 ++--
.../assessment/LLMUsabilityMetricCalculator.java | 9 +-
.../LLMUsabilityMetricCalculatorTest.java | 20 ++-
.../scenario/LLMUsabilityScenarioCatalog.java | 18 +--
.../scenario/LLMUsabilityScenarioCatalogTest.java | 9 +-
12 files changed, 110 insertions(+), 322 deletions(-)
diff --git a/.github/workflows/jdk21-subchain-ci.yml
b/.github/workflows/jdk21-subchain-ci.yml
index ff027793a1a..4e12ad1a33c 100644
--- a/.github/workflows/jdk21-subchain-ci.yml
+++ b/.github/workflows/jdk21-subchain-ci.yml
@@ -15,7 +15,7 @@
# limitations under the License.
#
-name: JDK 21 Subchain - E2E
+name: JDK 21 Subchain - CI
on:
pull_request:
@@ -24,15 +24,10 @@ on:
- '.github/workflows/jdk21-subchain-ci.yml'
- 'pom.xml'
- 'mcp/**'
- - 'distribution/pom.xml'
- - 'distribution/mcp/**'
- - 'test/e2e/pom.xml'
- - 'test/e2e/mcp/**'
- - '.github/workflows/mcp-build.yml'
workflow_dispatch:
concurrency:
- group: jdk21-subchain-e2e-${{ github.workflow }}-${{ github.ref }}
+ group: jdk21-subchain-ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
@@ -42,148 +37,24 @@ env:
MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false
-Dmaven.wagon.http.retryHandler.class=standard
-Dmaven.wagon.http.retryHandler.count=3 -Dspotless.apply.skip=true
jobs:
- global-environment:
- name: Import Global Environment
- uses: ./.github/workflows/required-reusable.yml
-
- detect-changed-files:
- name: Detect Changed Files
+ mcp-unit-tests:
+ name: MCP - Unit Tests
if: github.repository == 'apache/shardingsphere'
runs-on: ubuntu-latest
- timeout-minutes: 5
- steps:
- - uses: actions/[email protected]
- - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d
- id: filter
- with:
- token: ${{ github.token }}
- filters: |
- mcp_http_contract:
- - '.github/workflows/jdk21-subchain-ci.yml'
- - 'pom.xml'
- - 'mcp/**'
- - 'test/e2e/pom.xml'
- - 'test/e2e/mcp/**'
- mcp_mysql:
- - '.github/workflows/jdk21-subchain-ci.yml'
- - 'pom.xml'
- - 'mcp/**'
- - 'test/e2e/pom.xml'
- - 'test/e2e/mcp/**'
- mcp_distribution:
- - '.github/workflows/jdk21-subchain-ci.yml'
- - '.github/workflows/mcp-build.yml'
- - 'pom.xml'
- - 'mcp/**'
- - 'distribution/pom.xml'
- - 'distribution/mcp/**'
- - 'test/e2e/pom.xml'
- - 'test/e2e/mcp/**'
- - name: Logs
- run: |
- echo "changed-categories=${{ steps.filter.outputs.changes }}"
- echo "mcp-http-contract=${{ steps.filter.outputs.mcp_http_contract
}}"
- echo "mcp-mysql=${{ steps.filter.outputs.mcp_mysql }}"
- echo "mcp-distribution=${{ steps.filter.outputs.mcp_distribution }}"
- outputs:
- mcp_http_contract: ${{ steps.filter.outputs.mcp_http_contract }}
- mcp_mysql: ${{ steps.filter.outputs.mcp_mysql }}
- mcp_distribution: ${{ steps.filter.outputs.mcp_distribution }}
-
- mcp-http-contract-e2e:
- name: MCP E2E - HTTP Contract
- if: github.repository == 'apache/shardingsphere' && (github.event_name ==
'workflow_dispatch' || needs.detect-changed-files.outputs.mcp_http_contract ==
'true')
- needs: [ global-environment, detect-changed-files ]
- runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/[email protected]
- uses: ./.github/workflows/resources/actions/setup-build-environment
with:
java-version: '21'
- cache-prefix: ${{
needs.global-environment.outputs.GLOBAL_CACHE_PREFIX }}
cache-suffix: 'mcp'
- enable-docker-setup: 'true'
- - name: Run MCP HTTP Contract E2E
- run: ./mvnw -pl mcp,test/e2e/mcp -am install -DskipITs
-Dspotless.skip=true -Dsurefire.failIfNoSpecifiedTests=false
-Dmcp.e2e.contract.enabled=true -B -ntp
-
- mcp-mysql-e2e:
- name: MCP E2E - MySQL HTTP and STDIO
- if: github.repository == 'apache/shardingsphere' && (github.event_name ==
'workflow_dispatch' || needs.detect-changed-files.outputs.mcp_mysql == 'true')
- needs: [ global-environment, detect-changed-files ]
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - uses: actions/[email protected]
- - uses: ./.github/workflows/resources/actions/setup-build-environment
- with:
- java-version: '21'
- cache-prefix: ${{
needs.global-environment.outputs.GLOBAL_CACHE_PREFIX }}
- cache-suffix: 'mcp'
- enable-docker-setup: 'true'
- - name: Install MCP E2E Test Dependencies
- run: ./mvnw -pl test/e2e/mcp -am install -DskipTests -DskipITs
-Dspotless.skip=true -B -ntp
- - name: Run MCP MySQL HTTP and STDIO E2E
- run: >
- ./mvnw -pl test/e2e/mcp test
- -DskipITs -Dspotless.skip=true
-
-Dtest=ProductionMySQLRuntimeE2ETest,HttpProductionProxyEncryptWorkflowE2ETest,HttpProductionProxyMaskWorkflowE2ETest
- -Dsurefire.failIfNoSpecifiedTests=true
- -Dmcp.e2e.production.mysql.enabled=true
- -Dmcp.e2e.production.stdio.enabled=true
- -B -ntp
-
- mcp-distribution-e2e:
- name: MCP E2E - Distribution
- if: github.repository == 'apache/shardingsphere' && (github.event_name ==
'workflow_dispatch' || needs.detect-changed-files.outputs.mcp_distribution ==
'true')
- needs: [ global-environment, detect-changed-files ]
- runs-on: ubuntu-latest
- timeout-minutes: 60
- steps:
- - uses: actions/[email protected]
- - uses: ./.github/workflows/resources/actions/setup-build-environment
- with:
- java-version: '21'
- cache-prefix: ${{
needs.global-environment.outputs.GLOBAL_CACHE_PREFIX }}
- cache-suffix: 'mcp'
- enable-docker-setup: 'true'
- - name: Test MCP Registry Metadata Command
- run: ./mvnw -pl mcp/registry -am -DskipITs -Dspotless.skip=true
-Dtest=MCPRegistryMetadataCommandTest -Dsurefire.failIfNoSpecifiedTests=false
install -B -ntp
- - name: Package MCP Distribution
- run: ./mvnw -pl distribution/mcp -am -DskipTests package -B -ntp
- - name: Build MCP Docker Image
- run: docker build -f distribution/mcp/Dockerfile -t
shardingsphere-mcp-ci:local distribution/mcp/target
- - name: Validate MCP Registry Metadata
- shell: bash
- run: |
- set -euo pipefail
- DISTRIBUTION_HOME=$(find distribution/mcp/target -maxdepth 1 -type d
-name 'apache-shardingsphere-mcp-*' | head -n 1)
- if [[ -z "${DISTRIBUTION_HOME}" ]]; then
- echo "Packaged MCP distribution was not found." 1>&2
- exit 1
- fi
- test -f "${DISTRIBUTION_HOME}/conf/mcp-http.yaml"
- test -f "${DISTRIBUTION_HOME}/conf/mcp-stdio.yaml"
- ./mvnw -pl mcp/registry -DskipTests -DskipITs -Dspotless.skip=true \
-
-Dexec.mainClass=org.apache.shardingsphere.mcp.registry.MCPRegistryMetadataCommand
\
- -Dexec.args="--validate-only --allow-snapshot --dockerfile-path
distribution/mcp/Dockerfile" \
- compile exec:java -B -ntp
- - name: Install MCP E2E Test Dependencies
- run: ./mvnw -pl test/e2e/mcp -am install -DskipTests -DskipITs
-Dspotless.skip=true -B -ntp
- - name: Run MCP Distribution E2E
+ - name: Run MCP Unit Tests
shell: bash
run: |
set -euo pipefail
- DISTRIBUTION_HOME=$(find distribution/mcp/target -maxdepth 1 -type d
-name 'apache-shardingsphere-mcp-*' | head -n 1)
- if [[ -z "${DISTRIBUTION_HOME}" ]]; then
- echo "Packaged MCP distribution was not found." 1>&2
- exit 1
- fi
- DISTRIBUTION_HOME=$(cd "${DISTRIBUTION_HOME}" && pwd)
- ./mvnw -pl test/e2e/mcp test -DskipITs -Dspotless.skip=true \
- -Dtest=PackagedDistributionE2ETest \
- -Dsurefire.failIfNoSpecifiedTests=true \
- -Dmcp.e2e.distribution.enabled=true \
- -Dmcp.e2e.container.image=shardingsphere-mcp-ci:local \
- -Dmcp.distribution.home="${DISTRIBUTION_HOME}" \
+
MCP_MODULES=mcp/api,mcp/support,mcp/core,mcp/features/encrypt,mcp/features/mask
+ MCP_MODULES="${MCP_MODULES},mcp/bootstrap,mcp/registry"
+ ./mvnw -pl "${MCP_MODULES}" -am install \
+ -DskipITs -Dspotless.skip=true \
+ -Dsurefire.failIfNoSpecifiedTests=false \
-B -ntp
diff --git a/.github/workflows/mcp-llm-usability-e2e.yml
b/.github/workflows/mcp-e2e.yml
similarity index 72%
rename from .github/workflows/mcp-llm-usability-e2e.yml
rename to .github/workflows/mcp-e2e.yml
index 88222c6cc94..e592908f177 100644
--- a/.github/workflows/mcp-llm-usability-e2e.yml
+++ b/.github/workflows/mcp-e2e.yml
@@ -15,7 +15,7 @@
# limitations under the License.
#
-name: MCP - LLM Usability E2E
+name: MCP - E2E
on:
pull_request:
@@ -26,7 +26,8 @@ on:
- synchronize
- ready_for_review
paths:
- - '.github/workflows/mcp-llm-usability-e2e.yml'
+ - '.github/workflows/mcp-e2e.yml'
+ - 'pom.xml'
- 'mcp/**'
- 'test/e2e/pom.xml'
- 'test/e2e/mcp/**'
@@ -35,7 +36,7 @@ on:
workflow_dispatch:
concurrency:
- group: mcp-llm-usability-e2e-${{ github.workflow }}-${{ github.ref }}
+ group: mcp-e2e-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
@@ -50,14 +51,9 @@ env:
MCP_LLM_BASE_SERVER_IMAGE_DIGEST:
sha256:988d2695631987e28a29d98970aaf0e979e23b843a26824abb790ac4245d1d57
jobs:
- global-environment:
- name: Import Global Environment
- uses: ./.github/workflows/required-reusable.yml
-
- mcp-llm-usability-e2e:
- name: MCP - LLM Usability E2E
+ mcp-mysql-runtime-e2e:
+ name: MCP - MySQL Runtime E2E
if: github.repository == 'apache/shardingsphere'
- needs: global-environment
runs-on: ubuntu-latest
timeout-minutes: 60
env:
@@ -67,8 +63,7 @@ jobs:
- uses: ./.github/workflows/resources/actions/setup-build-environment
with:
java-version: '21'
- cache-prefix: ${{
needs.global-environment.outputs.GLOBAL_CACHE_PREFIX }}
- cache-suffix: 'mcp-llm-usability-e2e'
+ cache-suffix: 'mcp-e2e'
cache-save-enabled: 'false'
enable-docker-setup: 'true'
- name: Set Up Docker Buildx
@@ -92,15 +87,28 @@ jobs:
cache-to: type=gha,mode=max,scope=mcp-llm-runtime,ignore-error=true
- name: Build MCP E2E Test Dependencies
run: ./mvnw -pl test/e2e/mcp -am install -DskipTests -DskipITs
-Dspotless.skip=true -B -ntp
- - name: Verify MCP LLM Usability Selector
- run: test -f
test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteE2ETest.java
- - name: Run MCP LLM Usability Suite
- run: ./mvnw -pl test/e2e/mcp -Pllm-e2e test -DskipITs
-Dspotless.skip=true -Dtest=LLMUsabilitySuiteE2ETest
-Dsurefire.failIfNoSpecifiedTests=true -B -ntp
- - name: Upload MCP LLM Artifacts
+ - name: Run MCP MySQL Runtime E2E
+ shell: bash
+ run: |
+ set -euo pipefail
+
MCP_E2E_TESTS=HttpTransportContractE2ETest,HttpTransportProtocolContractE2ETest,HttpTransportBaselineContractE2ETest
+ MCP_E2E_TESTS="${MCP_E2E_TESTS},ProductionMySQLRuntimeE2ETest"
+
MCP_E2E_TESTS="${MCP_E2E_TESTS},HttpProductionProxyEncryptWorkflowE2ETest"
+
MCP_E2E_TESTS="${MCP_E2E_TESTS},HttpProductionProxyMaskWorkflowE2ETest,LLMUsabilitySuiteE2ETest"
+ ./mvnw -pl test/e2e/mcp test -DskipITs -Dspotless.skip=true \
+ -Dtest="${MCP_E2E_TESTS}" \
+ -Dsurefire.failIfNoSpecifiedTests=true \
+ -Dmcp.e2e.contract.enabled=true \
+ -Dmcp.e2e.production.mysql.enabled=true \
+ -Dmcp.e2e.production.stdio.enabled=true \
+ -Dmcp.e2e.llm.enabled=true \
+ -Dmcp.e2e.llm.excludedGroups= \
+ -B -ntp
+ - name: Upload MCP E2E Artifacts
if: always()
uses: actions/upload-artifact@v6
with:
- name: mcp-llm-usability-e2e-${{ github.run_id }}-${{
github.run_attempt }}
+ name: mcp-e2e-${{ github.run_id }}-${{ github.run_attempt }}
path: |
test/e2e/mcp/target/llm-e2e
test/e2e/mcp/target/surefire-reports
diff --git a/.github/workflows/mcp-llm-e2e.yml
b/.github/workflows/mcp-llm-e2e.yml
deleted file mode 100644
index 21b9193c21a..00000000000
--- a/.github/workflows/mcp-llm-e2e.yml
+++ /dev/null
@@ -1,107 +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.
-#
-
-name: MCP - LLM E2E
-
-on:
- pull_request:
- branches: [ master ]
- types:
- - opened
- - reopened
- - synchronize
- - ready_for_review
- paths:
- - '.github/workflows/mcp-llm-e2e.yml'
- - 'mcp/**'
- - 'test/e2e/pom.xml'
- - 'test/e2e/mcp/**'
- schedule:
- - cron: '0 17 * * 1-5'
- workflow_dispatch:
-
-concurrency:
- group: mcp-llm-e2e-${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-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 -Dspotless.apply.skip=true
- MCP_LLM_READY_TIMEOUT_SECONDS: '900'
- MCP_LLM_REQUEST_TIMEOUT_SECONDS: '300'
- MCP_LLM_ARTIFACT_ROOT: test/e2e/mcp/target/llm-e2e
- MCP_LLM_SERVER_IMAGE: apache/shardingsphere-mcp-llm-runtime:local
- MCP_LLM_BASE_SERVER_IMAGE_DIGEST:
sha256:988d2695631987e28a29d98970aaf0e979e23b843a26824abb790ac4245d1d57
-
-jobs:
- global-environment:
- name: Import Global Environment
- uses: ./.github/workflows/required-reusable.yml
-
- mcp-llm-e2e:
- name: MCP - LLM E2E
- if: github.repository == 'apache/shardingsphere'
- needs: global-environment
- runs-on: ubuntu-latest
- timeout-minutes: 60
- env:
- MCP_LLM_RUN_ID: gha-${{ github.run_id }}-${{ github.run_attempt }}
- steps:
- - uses: actions/[email protected]
- - uses: ./.github/workflows/resources/actions/setup-build-environment
- with:
- java-version: '21'
- cache-prefix: ${{
needs.global-environment.outputs.GLOBAL_CACHE_PREFIX }}
- cache-suffix: 'mcp-llm-e2e'
- cache-save-enabled: 'false'
- enable-docker-setup: 'true'
- - name: Set Up Docker Buildx
- uses: docker/setup-buildx-action@v3
- - name: Show Docker Buildx Version
- run: docker buildx version
- - name: Check Docker Environment
- run: |
- docker version
- docker system df
- - name: Build MCP LLM Runtime Image
- uses: docker/build-push-action@v6
- with:
- context: test/e2e/mcp/src/test/resources/docker/llm-runtime
- file: test/e2e/mcp/src/test/resources/docker/llm-runtime/Dockerfile
- build-args: |
- BASE_IMAGE=ghcr.io/ggml-org/llama.cpp@${{
env.MCP_LLM_BASE_SERVER_IMAGE_DIGEST }}
- tags: ${{ env.MCP_LLM_SERVER_IMAGE }}
- load: true
- cache-from: type=gha,scope=mcp-llm-runtime
- cache-to: type=gha,mode=max,scope=mcp-llm-runtime,ignore-error=true
- - name: Build MCP E2E Test Dependencies
- run: ./mvnw -pl test/e2e/mcp -am install -DskipTests -DskipITs
-Dspotless.skip=true -B -ntp
- - name: Verify MCP LLM Smoke Selector
- run: test -f
test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/smoke/LLMSmokeE2ETest.java
- - name: Run MCP LLM Smoke
- run: ./mvnw -pl test/e2e/mcp -Pllm-e2e test -DskipITs
-Dspotless.skip=true -Dtest=LLMSmokeE2ETest
-Dsurefire.failIfNoSpecifiedTests=true -B -ntp
- - name: Upload MCP LLM Artifacts
- if: always()
- uses: actions/upload-artifact@v6
- with:
- name: mcp-llm-e2e-${{ github.run_id }}-${{ github.run_attempt }}
- path: |
- test/e2e/mcp/target/llm-e2e
- test/e2e/mcp/target/surefire-reports
- if-no-files-found: warn
diff --git a/.github/workflows/mcp-build.yml b/.github/workflows/mcp-release.yml
similarity index 96%
rename from .github/workflows/mcp-build.yml
rename to .github/workflows/mcp-release.yml
index b953630ad1d..e2a2c07c1ce 100644
--- a/.github/workflows/mcp-build.yml
+++ b/.github/workflows/mcp-release.yml
@@ -15,7 +15,7 @@
# limitations under the License.
#
-name: MCP - Build
+name: MCP - Release
on:
release:
@@ -28,7 +28,7 @@ on:
type: string
concurrency:
- group: mcp-build-${{ github.event_name }}-${{ github.event_name == 'release'
&& github.event.release.tag_name || inputs.version }}
+ group: mcp-release-${{ github.event_name }}-${{ github.event_name ==
'release' && github.event.release.tag_name || inputs.version }}
cancel-in-progress: true
permissions:
@@ -47,8 +47,8 @@ jobs:
name: Import Global Environment
uses: ./.github/workflows/required-reusable.yml
- mcp-build:
- name: MCP - Build
+ mcp-release:
+ name: MCP - Release
if: github.repository == 'apache/shardingsphere'
needs: global-environment
runs-on: ubuntu-latest
@@ -85,9 +85,9 @@ jobs:
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
echo "identifier=${{ env.MCP_IMAGE }}:${VERSION}" >>
"${GITHUB_OUTPUT}"
echo "tags=${TAGS}" >> "${GITHUB_OUTPUT}"
- - name: Run and Install JDK 21 Subchain Tests
+ - name: Run and Install MCP JDK 21 Subchain Tests
run: ./mvnw -pl mcp,test/e2e/mcp -am install -DskipITs
-Dspotless.skip=true -Dsurefire.failIfNoSpecifiedTests=false -B -ntp
- - name: Package JDK 21 Subchain Distribution
+ - name: Package MCP Distribution
run: ./mvnw -pl distribution/mcp -am -DskipTests package -B -ntp
- name: Build Local MCP Docker Image
run: docker build -f distribution/mcp/Dockerfile -t
shardingsphere-mcp-release:local distribution/mcp/target
diff --git a/docs/community/content/involved/release/shardingsphere.cn.md
b/docs/community/content/involved/release/shardingsphere.cn.md
index 4bcefce45c4..9ed64cef53d 100644
--- a/docs/community/content/involved/release/shardingsphere.cn.md
+++ b/docs/community/content/involved/release/shardingsphere.cn.md
@@ -605,7 +605,7 @@ docker logout
3.8 ShardingSphere MCP 发布
-ShardingSphere MCP 通过仓库中的
[`.github/workflows/mcp-build.yml`](https://github.com/apache/shardingsphere/blob/master/.github/workflows/mcp-build.yml)
workflow 发布。
+ShardingSphere MCP 通过仓库中的
[`.github/workflows/mcp-release.yml`](https://github.com/apache/shardingsphere/blob/master/.github/workflows/mcp-release.yml)
workflow 发布。
该 workflow 会构建 MCP 发行包、把
`ghcr.io/apache/shardingsphere-mcp:${RELEASE.VERSION}` 推送到 GHCR,
稳定版额外更新 `latest`,然后通过 GitHub OIDC 把 `mcp/server.json` 发布到官方 MCP Registry。
@@ -617,7 +617,7 @@ ShardingSphere MCP 通过仓库中的
[`.github/workflows/mcp-build.yml`](https:
GitHub release 发布后:
-- 等待 `MCP - Build` workflow 成功完成
+- 等待 `MCP - Release` workflow 成功完成
- 确认 [GitHub
Packages](https://github.com/apache/shardingsphere/pkgs/container/shardingsphere-mcp)
中存在 `ghcr.io/apache/shardingsphere-mcp:${RELEASE.VERSION}`
- 通过下面的命令确认官方 MCP Registry 已返回该 server 的 metadata:
diff --git a/docs/community/content/involved/release/shardingsphere.en.md
b/docs/community/content/involved/release/shardingsphere.en.md
index f7442129528..836fbb7ed13 100644
--- a/docs/community/content/involved/release/shardingsphere.en.md
+++ b/docs/community/content/involved/release/shardingsphere.en.md
@@ -610,7 +610,7 @@ docker logout
3.8 ShardingSphere MCP publication
-ShardingSphere MCP is published through the repository workflow
[`.github/workflows/mcp-build.yml`](https://github.com/apache/shardingsphere/blob/master/.github/workflows/mcp-build.yml).
+ShardingSphere MCP is published through the repository workflow
[`.github/workflows/mcp-release.yml`](https://github.com/apache/shardingsphere/blob/master/.github/workflows/mcp-release.yml).
The workflow builds the MCP distribution, pushes
`ghcr.io/apache/shardingsphere-mcp:${RELEASE.VERSION}` to GHCR,
pushes `latest` for stable releases, and then publishes `mcp/server.json` to
the official MCP Registry through GitHub OIDC.
@@ -622,7 +622,7 @@ Edit release version and release notes, select `Set as the
latest release`, clic
After the GitHub release is published:
-- wait for the `MCP - Build` workflow to finish successfully
+- wait for the `MCP - Release` workflow to finish successfully
- confirm [GitHub
Packages](https://github.com/apache/shardingsphere/pkgs/container/shardingsphere-mcp)
contains `ghcr.io/apache/shardingsphere-mcp:${RELEASE.VERSION}`
- confirm the official MCP Registry returns the published metadata:
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 cd7f5397003..37bfda3795f 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
@@ -5,7 +5,7 @@ weight = 5
chapter = true
+++
-本章说明 ShardingSphere-MCP 的端到端契约验证和 LLM smoke 验证。
+本章说明 ShardingSphere-MCP 的端到端契约验证和 LLM usability 验证。
## 范围
@@ -16,7 +16,7 @@ chapter = true
- STDIO runtime。
- MCP baseline contract。
- Tool/resource/prompt/completion discovery。
-- 真实模型驱动的 MCP smoke。
+- 真实模型驱动的 MCP usability。
- Encrypt 和 Mask workflow 可用性验证。
## 本地准备
@@ -48,12 +48,21 @@ sh
test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh --dry-run
sh test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh
```
-## 运行 LLM Smoke
+## 运行 MCP Runtime E2E
```bash
-./mvnw -pl test/e2e/mcp -Pllm-e2e test -DskipITs -Dspotless.skip=true \
- -Dtest=LLMSmokeE2ETest \
- -Dsurefire.failIfNoSpecifiedTests=true
+MCP_E2E_TESTS=HttpTransportContractE2ETest,HttpTransportProtocolContractE2ETest,HttpTransportBaselineContractE2ETest
+MCP_E2E_TESTS="${MCP_E2E_TESTS},ProductionMySQLRuntimeE2ETest"
+MCP_E2E_TESTS="${MCP_E2E_TESTS},HttpProductionProxyEncryptWorkflowE2ETest"
+MCP_E2E_TESTS="${MCP_E2E_TESTS},HttpProductionProxyMaskWorkflowE2ETest,LLMUsabilitySuiteE2ETest"
+./mvnw -pl test/e2e/mcp test -DskipITs -Dspotless.skip=true \
+ -Dtest="${MCP_E2E_TESTS}" \
+ -Dsurefire.failIfNoSpecifiedTests=true \
+ -Dmcp.e2e.contract.enabled=true \
+ -Dmcp.e2e.production.mysql.enabled=true \
+ -Dmcp.e2e.production.stdio.enabled=true \
+ -Dmcp.e2e.llm.enabled=true \
+ -Dmcp.e2e.llm.excludedGroups=
```
## 运行 LLM Usability Suite
@@ -70,7 +79,7 @@ sh
test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh
```bash
./mvnw -pl test/e2e/mcp -Pllm-e2e test -DskipITs -Dspotless.skip=true \
- -Dtest=LLMSmokeE2ETest \
+ -Dtest=LLMUsabilitySuiteE2ETest \
-Dmcp.llm.runtime-mode=external-debug \
-Dmcp.llm.base-url=http://127.0.0.1:8080/v1 \
-Dsurefire.failIfNoSpecifiedTests=true
@@ -88,8 +97,7 @@ test/e2e/mcp/target/llm-e2e/
GitHub Actions 入口:
-- `.github/workflows/mcp-llm-e2e.yml`
-- `.github/workflows/mcp-llm-usability-e2e.yml`
+- `.github/workflows/mcp-e2e.yml`
-这两条检查用于提供额外可见性,不应单独配置成 branch protection 或 ruleset 的 required check。
+这条 workflow 是 MCP runtime E2E 的必跑入口。
如果超大 PR 因 path filter 限制漏触发,可以使用 `workflow_dispatch` 手动补充 evidence。
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 695fdc81d27..9a73528901f 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
@@ -5,7 +5,7 @@ weight = 5
chapter = true
+++
-This chapter describes ShardingSphere-MCP end-to-end contract validation and
LLM smoke validation.
+This chapter describes ShardingSphere-MCP end-to-end contract validation and
LLM usability validation.
## Scope
@@ -16,7 +16,7 @@ This chapter describes ShardingSphere-MCP end-to-end contract
validation and LLM
- STDIO runtime.
- MCP baseline contract.
- Tool/resource/prompt/completion discovery.
-- Real-model MCP smoke.
+- Real-model MCP usability.
- Encrypt and Mask workflow usability validation.
## Local preparation
@@ -48,12 +48,21 @@ Build the local runtime image:
sh test/e2e/mcp/src/test/resources/docker/llm-runtime/build-local.sh
```
-## Run LLM Smoke
+## Run MCP Runtime E2E
```bash
-./mvnw -pl test/e2e/mcp -Pllm-e2e test -DskipITs -Dspotless.skip=true \
- -Dtest=LLMSmokeE2ETest \
- -Dsurefire.failIfNoSpecifiedTests=true
+MCP_E2E_TESTS=HttpTransportContractE2ETest,HttpTransportProtocolContractE2ETest,HttpTransportBaselineContractE2ETest
+MCP_E2E_TESTS="${MCP_E2E_TESTS},ProductionMySQLRuntimeE2ETest"
+MCP_E2E_TESTS="${MCP_E2E_TESTS},HttpProductionProxyEncryptWorkflowE2ETest"
+MCP_E2E_TESTS="${MCP_E2E_TESTS},HttpProductionProxyMaskWorkflowE2ETest,LLMUsabilitySuiteE2ETest"
+./mvnw -pl test/e2e/mcp test -DskipITs -Dspotless.skip=true \
+ -Dtest="${MCP_E2E_TESTS}" \
+ -Dsurefire.failIfNoSpecifiedTests=true \
+ -Dmcp.e2e.contract.enabled=true \
+ -Dmcp.e2e.production.mysql.enabled=true \
+ -Dmcp.e2e.production.stdio.enabled=true \
+ -Dmcp.e2e.llm.enabled=true \
+ -Dmcp.e2e.llm.excludedGroups=
```
## Run LLM Usability Suite
@@ -70,7 +79,7 @@ For local debugging only, connect to an already running
OpenAI-compatible endpoi
```bash
./mvnw -pl test/e2e/mcp -Pllm-e2e test -DskipITs -Dspotless.skip=true \
- -Dtest=LLMSmokeE2ETest \
+ -Dtest=LLMUsabilitySuiteE2ETest \
-Dmcp.llm.runtime-mode=external-debug \
-Dmcp.llm.base-url=http://127.0.0.1:8080/v1 \
-Dsurefire.failIfNoSpecifiedTests=true
@@ -88,8 +97,7 @@ test/e2e/mcp/target/llm-e2e/
GitHub Actions entry points:
-- `.github/workflows/mcp-llm-e2e.yml`
-- `.github/workflows/mcp-llm-usability-e2e.yml`
+- `.github/workflows/mcp-e2e.yml`
-These checks provide additional visibility and should not be configured as
required branch protection or ruleset checks by themselves.
+This workflow is the mandatory MCP runtime E2E entry point.
If a very large PR misses a path-filter match, use `workflow_dispatch` to add
manual evidence.
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculator.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculator.java
index f012d9f56b6..c9749ded6f5 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculator.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculator.java
@@ -408,7 +408,14 @@ public final class LLMUsabilityMetricCalculator {
private boolean isMachineAction(final Map<?, ?> action) {
String type = Objects.toString(action.get("type"), "");
- return "resource_read".equals(type) || "tool_call".equals(type) ||
"completion".equals(type);
+ if (!"resource_read".equals(type) && !"tool_call".equals(type) &&
!"completion".equals(type)) {
+ return false;
+ }
+ if (!"tool_call".equals(type) || !(action.get("arguments") instanceof
Map)) {
+ return true;
+ }
+ String executionMode = Objects.toString(((Map<?, ?>)
action.get("arguments")).get("execution_mode"), "");
+ return !"execute".equals(executionMode) &&
!"review-then-execute".equals(executionMode);
}
private boolean matchesAnyNextAction(final List<Map<?, ?>> actions, final
MCPInteractionTraceRecord current, final MCPInteractionTraceRecord next) {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculatorTest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculatorTest.java
index 69b51bf944a..6506a4711a7 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculatorTest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/assessment/LLMUsabilityMetricCalculatorTest.java
@@ -24,6 +24,8 @@ import
org.apache.shardingsphere.test.e2e.mcp.llm.suite.usability.scenario.LLMUs
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionActionNames;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionTraceRecord;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
import java.util.List;
import java.util.Map;
@@ -69,13 +71,19 @@ class LLMUsabilityMetricCalculatorTest {
assertTrue(actual.isNextActionFollowed());
}
- @Test
- void assertEvaluateScenarioWithToolCallNextActionFollowed() {
+ @ParameterizedTest(name = "{0}")
+ @CsvSource({
+ "regular tool call guidance, database_gateway_execute_update, '',
database_gateway_execute_update",
+ "execute side-effect guidance, database_gateway_execute_update,
execute, database_gateway_execute_query",
+ "review-then-execute side-effect guidance,
database_gateway_apply_workflow, review-then-execute,
database_gateway_execute_query"
+ })
+ void assertEvaluateScenarioWithToolCallNextActionGuidance(final String
name, final String nextActionToolName, final String executionMode, final String
nextToolName) {
+ Map<String, Object> nextAction = executionMode.isEmpty()
+ ? Map.of("type", "tool_call", "tool_name", nextActionToolName)
+ : Map.of("type", "tool_call", "tool_name", nextActionToolName,
"arguments", Map.of("execution_mode", executionMode));
List<MCPInteractionTraceRecord> trace = List.of(
- createToolCall(1, "database_gateway_execute_update",
Map.of("next_actions", List.of(Map.of(
- "type", "tool_call",
- "tool_name", "database_gateway_execute_update")))),
- createToolCall(2, "database_gateway_execute_update",
Map.of()));
+ createToolCall(1, nextActionToolName, Map.of("next_actions",
List.of(nextAction))),
+ createToolCall(2, nextToolName, Map.of()));
LLMUsabilityScenarioResult actual = new
LLMUsabilityMetricCalculator().evaluateScenario(createScenario(),
createArtifactBundle(trace));
assertTrue(actual.isNextActionFollowed());
assertFalse(actual.isApprovalViolation());
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalog.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalog.java
index c03378490e5..a6533fde156 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalog.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalog.java
@@ -151,12 +151,11 @@ public final class LLMUsabilityScenarioCatalog {
List.of(LLMUsabilityScenario.NATURAL_TASK_TAG, "extended",
"recovery"),
new LLME2EScenario("extended-recovery-missing-database-" +
runtimeKind, SYSTEM_PROMPT,
"The user only remembers schema `" + schemaName + "`
and table `" + tableName + "`. Search metadata broadly with query `" + tableName
- + "` and object type `table` without setting
database or schema. If more than one database contains `" + tableName + "`,
choose logical database `"
- + databaseName + "` and do not use
`analytics_db`. Then verify `" + query + "`.",
+ + "` and object type `table` without setting
database or schema. Then verify `" + query + "`.",
createAnswer(databaseName, schemaName, tableName,
query, totalOrders),
List.of("database_gateway_search_metadata",
"database_gateway_execute_query"),
List.of("database_gateway_search_metadata",
"database_gateway_execute_query")),
- List.of("database_gateway_search_metadata"), List.of(), false,
true, "ambiguous"));
+ List.of("database_gateway_search_metadata"), List.of(), false,
false));
result.add(createScenario("extended-recovery-bad-resource-" +
runtimeKind, LLMUsabilityDimension.RECOVERY, runtimeKind,
List.of(LLMUsabilityScenario.PROTOCOL_CONTRACT_TAG,
"extended", "recovery", "resource"),
new LLME2EScenario("extended-recovery-bad-resource-" +
runtimeKind, SYSTEM_PROMPT,
@@ -165,18 +164,7 @@ public final class LLMUsabilityScenarioCatalog {
createAnswer(databaseName, schemaName, tableName,
query, totalOrders),
List.of(MCPInteractionActionNames.READ_RESOURCE,
"database_gateway_execute_query"),
List.of(MCPInteractionActionNames.READ_RESOURCE,
"database_gateway_execute_query")),
- List.of(MCPInteractionActionNames.READ_RESOURCE),
List.of(tableResourceUri), true, true, "not_found"));
- if ("mysql".equals(runtimeKind)) {
-
result.add(createScenario("extended-recovery-unsupported-sequence-" +
runtimeKind, LLMUsabilityDimension.RECOVERY, runtimeKind,
- List.of(LLMUsabilityScenario.NATURAL_TASK_TAG, "extended",
"recovery", "unsupported-resource"),
- new
LLME2EScenario("extended-recovery-unsupported-sequence-" + runtimeKind,
SYSTEM_PROMPT,
- "The user asks about sequence metadata near `" +
tableName + "` on this MySQL runtime. Recover safely if sequence resources are
unsupported, then verify `"
- + query + "`." + toolContext,
- createAnswer(databaseName, schemaName, tableName,
query, totalOrders),
- List.of(MCPInteractionActionNames.READ_RESOURCE,
"database_gateway_execute_query"),
- List.of(MCPInteractionActionNames.READ_RESOURCE,
"database_gateway_execute_query")),
- List.of(MCPInteractionActionNames.READ_RESOURCE),
List.of(tableResourceUri), true, true, "not_found"));
- }
+ List.of(MCPInteractionActionNames.READ_RESOURCE),
List.of(tableResourceUri), true, true, "unknown_database"));
return result;
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalogTest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalogTest.java
index 5e176534175..6e6d328b2a8 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalogTest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalogTest.java
@@ -59,14 +59,11 @@ class LLMUsabilityScenarioCatalogTest {
"SELECT COUNT(*) AS total_orders FROM orders", 2);
Map<String, LLMUsabilityScenario> actualScenarios =
actual.stream().collect(Collectors.toMap(LLMUsabilityScenario::getScenarioId,
each -> each));
assertThat(actualScenarios.keySet(),
hasItems("extended-prompt-completion-inspect-mysql",
"extended-runtime-status-mysql", "extended-recovery-missing-database-mysql",
- "extended-recovery-unsupported-sequence-mysql"));
+ "extended-recovery-bad-resource-mysql"));
assertThat(actualScenarios.get("extended-runtime-status-mysql").getExpectedResourceUris(),
is(List.of("shardingsphere://runtime")));
-
assertTrue(actualScenarios.get("extended-recovery-missing-database-mysql").isRecoveryExpected());
-
assertThat(actualScenarios.get("extended-recovery-missing-database-mysql").getExpectedRecoveryCategory(),
is("ambiguous"));
+
assertFalse(actualScenarios.get("extended-recovery-missing-database-mysql").isRecoveryExpected());
assertTrue(actualScenarios.get("extended-recovery-bad-resource-mysql").isRecoveryExpected());
-
assertThat(actualScenarios.get("extended-recovery-bad-resource-mysql").getExpectedRecoveryCategory(),
is("not_found"));
-
assertTrue(actualScenarios.get("extended-recovery-unsupported-sequence-mysql").isRecoveryExpected());
-
assertThat(actualScenarios.get("extended-recovery-unsupported-sequence-mysql").getExpectedRecoveryCategory(),
is("not_found"));
+
assertThat(actualScenarios.get("extended-recovery-bad-resource-mysql").getExpectedRecoveryCategory(),
is("unknown_database"));
assertThat(actualScenarios.get("extended-prompt-completion-inspect-mysql").getLlmScenario().getUserPrompt(),
containsString("Use the MCP prompt list"));
assertTrue(actualScenarios.get("extended-recovery-missing-database-mysql").isNaturalTask());
assertTrue(actualScenarios.get("extended-prompt-completion-inspect-mysql").isProtocolContract());