This is an automated email from the ASF dual-hosted git repository.

FreeOnePlus pushed a commit to branch agent/proto-018-version-identity
in repository https://gitbox.apache.org/repos/asf/doris-mcp-server.git

commit 544028a45565d46e0bc2357147a4be0452ddbfd3
Author: FreeOnePlus <[email protected]>
AuthorDate: Wed Jul 29 18:53:06 2026 +0800

    fix: unify product version identity
---
 .env.example                                   |  2 +-
 doris_mcp_client/client.py                     |  7 ++++
 doris_mcp_server/__init__.py                   |  3 +-
 doris_mcp_server/{__init__.py => _version.py}  | 12 +-----
 doris_mcp_server/main.py                       | 20 +++++++---
 doris_mcp_server/multiworker_app.py            |  8 ++--
 doris_mcp_server/utils/config.py               |  7 ++--
 pyproject.toml                                 |  5 ++-
 test/integration/test_real_doris_transports.py |  7 ++++
 test/protocol/conformance_server.py            |  3 +-
 test/protocol/stdio_capability_server.py       |  3 +-
 test/protocol/test_mcp_v2_protocol.py          | 16 +++++++-
 test/protocol/test_multiworker_config.py       | 21 ++++++++++-
 test/test_product_identity.py                  | 51 ++++++++++++++++++++++++++
 test/tools/test_tools_operation_guard.py       |  3 +-
 uv.lock                                        |  3 +-
 16 files changed, 134 insertions(+), 37 deletions(-)

diff --git a/.env.example b/.env.example
index b347498..bc9cd56 100644
--- a/.env.example
+++ b/.env.example
@@ -401,7 +401,7 @@ ALERT_WEBHOOK_URL=
 
 # Basic server information
 SERVER_NAME=doris-mcp-server
-SERVER_VERSION=0.6.0
+# Product version is defined by the installed package and is not configurable.
 SERVER_PORT=3000
 
 # Temporary files directory
diff --git a/doris_mcp_client/client.py b/doris_mcp_client/client.py
index 92ee351..ba87984 100644
--- a/doris_mcp_client/client.py
+++ b/doris_mcp_client/client.py
@@ -39,6 +39,8 @@ from mcp.types import (
     Tool,
 )
 
+from doris_mcp_server import __version__
+
 # Configure logging
 logging.basicConfig(level=logging.INFO)
 logger = logging.getLogger(__name__)
@@ -463,6 +465,11 @@ def create_arg_parser() -> argparse.ArgumentParser:
     parser = argparse.ArgumentParser(
         description="Connect to an Apache Doris MCP server using SDK 2.0.",
     )
+    parser.add_argument(
+        "--version",
+        action="version",
+        version=f"%(prog)s {__version__}",
+    )
     parser.add_argument(
         "--transport",
         choices=["http", "stdio"],
diff --git a/doris_mcp_server/__init__.py b/doris_mcp_server/__init__.py
index 4913b58..711b22c 100644
--- a/doris_mcp_server/__init__.py
+++ b/doris_mcp_server/__init__.py
@@ -24,6 +24,7 @@ This package provides:
 - Enterprise-grade security and monitoring
 """
 
-__version__ = "0.6.1"
+from ._version import __version__
+
 __author__ = "Doris MCP Team"
 __description__ = "Apache Doris MCP Server Implementation"
diff --git a/doris_mcp_server/__init__.py b/doris_mcp_server/_version.py
similarity index 66%
copy from doris_mcp_server/__init__.py
copy to doris_mcp_server/_version.py
index 4913b58..55dd956 100644
--- a/doris_mcp_server/__init__.py
+++ b/doris_mcp_server/_version.py
@@ -14,16 +14,6 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-"""
-Doris MCP Server - A Model Context Protocol server for Apache Doris database 
integration.
-
-This package provides:
-- MCP protocol implementation for Apache Doris
-- Multi-transport support (stdio, SSE, streamable HTTP)
-- Comprehensive database tools and resources
-- Enterprise-grade security and monitoring
-"""
+"""Build-time product version source."""
 
 __version__ = "0.6.1"
-__author__ = "Doris MCP Team"
-__description__ = "Apache Doris MCP Server Implementation"
diff --git a/doris_mcp_server/main.py b/doris_mcp_server/main.py
index fac7aec..ac343a8 100644
--- a/doris_mcp_server/main.py
+++ b/doris_mcp_server/main.py
@@ -28,6 +28,7 @@ import logging
 import os
 import sys
 
+from ._version import __version__
 from .protocol import create_doris_mcp_server, create_transport_security
 from .tools.prompts_manager import DorisPromptsManager
 from .tools.resources_manager import DorisResourcesManager
@@ -65,7 +66,6 @@ def _multiworker_environment(
         "SERVER_HOST": host,
         "SERVER_PORT": str(port),
         "SERVER_NAME": config.server_name,
-        "SERVER_VERSION": config.server_version,
         "TRANSPORT": "http",
         "WORKERS": str(workers),
     }
@@ -206,7 +206,13 @@ class DorisServer:
             
             # Health check endpoint
             async def health_check(request):
-                return JSONResponse({"status": "healthy", "service": 
"doris-mcp-server"})
+                return JSONResponse(
+                    {
+                        "status": "healthy",
+                        "service": self.config.server_name,
+                        "version": __version__,
+                    }
+                )
             
             # OAuth endpoints
             from .auth.oauth_handlers import OAuthHandlers
@@ -443,6 +449,12 @@ Examples:
         """
     )
 
+    parser.add_argument(
+        "--version",
+        action="version",
+        version=f"%(prog)s {__version__}",
+    )
+
     parser.add_argument(
         "--transport",
         type=str,
@@ -530,10 +542,6 @@ def update_configuration(config: DorisConfig):
     server_name = os.getenv("SERVER_NAME")
     if server_name:
         config.server_name = server_name
-    server_version = os.getenv("SERVER_VERSION")
-    if server_version:
-        config.server_version = server_version
- 
     # database
     if cli_has("--doris-host", "--db-host"):
         config.database.host = args.doris_host
diff --git a/doris_mcp_server/multiworker_app.py 
b/doris_mcp_server/multiworker_app.py
index 8fae732..54b7418 100644
--- a/doris_mcp_server/multiworker_app.py
+++ b/doris_mcp_server/multiworker_app.py
@@ -25,12 +25,12 @@ robust architecture as the single-worker mode.
 
 import os
 from contextlib import asynccontextmanager
-from importlib.metadata import version as distribution_version
 
 from starlette.applications import Starlette
 from starlette.responses import JSONResponse
 from starlette.routing import Route
 
+from ._version import __version__
 from .protocol import create_doris_mcp_server, create_transport_security
 from .tools.prompts_manager import DorisPromptsManager
 from .tools.resources_manager import DorisResourcesManager
@@ -45,8 +45,6 @@ from .utils.config import (
 from .utils.db import DorisConnectionManager
 from .utils.security import DorisSecurityManager
 
-MCP_VERSION = distribution_version("mcp")
-
 # Global variables for worker-specific instances
 _worker_server = None
 _worker_session_manager = None
@@ -158,7 +156,7 @@ async def health_check(request):
         "worker_pid": os.getpid(),
         "worker_mode": "multi-process-full-mcp",
         "mcp_initialized": _worker_initialized,
-        "mcp_version": MCP_VERSION
+        "version": __version__,
     })
 
 # OAuth and Token handlers (initialize after worker setup)
@@ -293,7 +291,7 @@ async def root_info(request):
         "mode": "multi-worker-full-mcp",
         "worker_pid": os.getpid(),
         "mcp_initialized": _worker_initialized,
-        "mcp_version": MCP_VERSION,
+        "version": __version__,
         "endpoints": {
             "health": "/health",
             "mcp": "/mcp"
diff --git a/doris_mcp_server/utils/config.py b/doris_mcp_server/utils/config.py
index 630f1d8..7031aca 100644
--- a/doris_mcp_server/utils/config.py
+++ b/doris_mcp_server/utils/config.py
@@ -29,6 +29,8 @@ from pathlib import Path
 from typing import Any
 from urllib.parse import urlparse
 
+from .._version import __version__
+
 try:
     from dotenv import load_dotenv
 except ImportError:
@@ -492,7 +494,7 @@ class DorisConfig:
 
     # Basic configuration
     server_name: str = "doris-mcp-server"
-    server_version: str = "0.6.1"
+    server_version: str = field(default=__version__, init=False)
     server_host: str = "localhost"
     server_port: int = 3000
     transport: str = "stdio"
@@ -918,7 +920,6 @@ class DorisConfig:
 
         # Server configuration
         config.server_name = os.getenv("SERVER_NAME", config.server_name)
-        config.server_version = os.getenv("SERVER_VERSION", 
config.server_version)
         server_host = os.getenv("SERVER_HOST", "").strip()
         if server_host:
             config.server_host = server_host
@@ -941,7 +942,7 @@ class DorisConfig:
         config = cls()
 
         # Update basic configuration
-        for key in ["server_name", "server_version", "server_port", 
"temp_files_dir", "transport", "workers"]:
+        for key in ["server_name", "server_port", "temp_files_dir", 
"transport", "workers"]:
             if key in config_data:
                 setattr(config, key, config_data[key])
                 _mark_source(config, key, "config_file")
diff --git a/pyproject.toml b/pyproject.toml
index b52fe79..b5d5650 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,7 +20,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "doris-mcp-server"
-version = "0.6.1"
+dynamic = ["version"]
 description = "Enterprise-grade Model Context Protocol (MCP) server 
implementation for Apache Doris"
 authors = [
     {name = "Yijia Su", email = "[email protected]"}
@@ -154,6 +154,9 @@ Changelog = 
"https://github.com/apache/doris-mcp-server/blob/main/CHANGELOG.md";
 doris-mcp-server = "doris_mcp_server.main:main_sync"
 doris-mcp-client = "doris_mcp_client.client:main"
 
+[tool.hatch.version]
+path = "doris_mcp_server/_version.py"
+
 [tool.hatch.build.targets.wheel]
 packages = ["doris_mcp_server", "doris_mcp_client"]
 
diff --git a/test/integration/test_real_doris_transports.py 
b/test/integration/test_real_doris_transports.py
index b875722..28afb28 100644
--- a/test/integration/test_real_doris_transports.py
+++ b/test/integration/test_real_doris_transports.py
@@ -45,6 +45,8 @@ import pytest
 from mcp import Client, StdioServerParameters
 from mcp.client.stdio import stdio_client
 
+from doris_mcp_server import __version__
+
 pytestmark = [
     pytest.mark.integration,
     pytest.mark.skipif(
@@ -229,6 +231,7 @@ async def _wait_for_http_server(
             try:
                 response = await client.get(f"http://127.0.0.1:{port}/health";)
                 if response.status_code == 200:
+                    assert response.json()["version"] == __version__
                     return
             except httpx.HTTPError:
                 pass
@@ -336,6 +339,10 @@ async def 
test_real_doris_read_write_permission_timeout_and_recovery(
         password=doris_sandbox.settings.password,
     )
     async with _transport_client(transport, admin_environment) as client:
+        assert client.server_info is not None
+        assert client.server_info.name == "doris-mcp-server"
+        assert client.server_info.version == __version__
+
         read_result, read_payload = await _exec_query(client, "SELECT 42 AS 
answer")
         assert read_result.is_error is False
         assert read_payload["success"] is True
diff --git a/test/protocol/conformance_server.py 
b/test/protocol/conformance_server.py
index 2cc8be8..f59b0a2 100644
--- a/test/protocol/conformance_server.py
+++ b/test/protocol/conformance_server.py
@@ -34,6 +34,7 @@ from mcp.types import (
     Tool,
 )
 
+from doris_mcp_server import __version__
 from doris_mcp_server.protocol import (
     create_doris_mcp_server,
     create_transport_security,
@@ -77,7 +78,7 @@ def create_conformance_server():
         tools_manager=CapabilityToolsManager(),
         prompts_manager=EmptyPromptsManager(),
         name="doris-mcp-conformance-test",
-        version="0.6.1",
+        version=__version__,
         logger=logging.getLogger(__name__),
         required_tool_capabilities={
             "test_missing_capability": 
ClientCapabilities(sampling=SamplingCapability())
diff --git a/test/protocol/stdio_capability_server.py 
b/test/protocol/stdio_capability_server.py
index 25847a7..d0d222e 100644
--- a/test/protocol/stdio_capability_server.py
+++ b/test/protocol/stdio_capability_server.py
@@ -33,6 +33,7 @@ from mcp.types import (
     Tool,
 )
 
+from doris_mcp_server import __version__
 from doris_mcp_server.protocol import create_doris_mcp_server
 from doris_mcp_server.tools.tools_manager import DorisToolsManager
 from doris_mcp_server.utils.analysis_tools import SQLAnalyzer
@@ -306,7 +307,7 @@ async def main() -> None:
         tools_manager=OneToolManager(),
         prompts_manager=EmptyPromptsManager(),
         name="doris-mcp-stdio-capability-test",
-        version="0.6.1",
+        version=__version__,
         logger=logging.getLogger(__name__),
         required_client_capabilities={
             "tools/list": ClientCapabilities(
diff --git a/test/protocol/test_mcp_v2_protocol.py 
b/test/protocol/test_mcp_v2_protocol.py
index 6ee460b..a050239 100644
--- a/test/protocol/test_mcp_v2_protocol.py
+++ b/test/protocol/test_mcp_v2_protocol.py
@@ -36,6 +36,7 @@ from mcp.types import (
     Tool,
 )
 
+from doris_mcp_server import __version__
 from doris_mcp_server.protocol import (
     create_doris_mcp_server,
     create_transport_security,
@@ -155,7 +156,7 @@ def create_test_server(
         tools_manager=tools_manager or StubToolsManager(),
         prompts_manager=StubPromptsManager(),
         name="doris-mcp-server",
-        version="0.6.1",
+        version=__version__,
         logger=logging.getLogger(__name__),
         required_client_capabilities=required_client_capabilities,
         required_tool_capabilities=required_tool_capabilities,
@@ -170,7 +171,7 @@ async def 
test_modern_and_legacy_clients_share_the_v2_protocol_core():
         assert modern.protocol_version == "2026-07-28"
         assert modern.server_info is not None
         assert modern.server_info.name == "doris-mcp-server"
-        assert modern.server_info.version == "0.6.1"
+        assert modern.server_info.version == __version__
         assert modern.session.discover_result is not None
         assert modern.session.discover_result.result_type == "complete"
 
@@ -218,6 +219,9 @@ async def 
test_modern_and_legacy_clients_share_the_v2_protocol_core():
 
     async with Client(server, mode="legacy") as legacy:
         assert legacy.protocol_version == "2025-11-25"
+        assert legacy.server_info is not None
+        assert legacy.server_info.name == "doris-mcp-server"
+        assert legacy.server_info.version == __version__
         assert [tool.name for tool in (await legacy.list_tools()).tools] == [
             "echo",
             "fail",
@@ -333,6 +337,11 @@ async def 
test_http_discover_is_stateless_and_unknown_method_does_not_kill_serve
         assert first.status_code == 200
         assert "mcp-session-id" not in first.headers
         assert first.json()["result"]["supportedVersions"] == ["2026-07-28"]
+        discover_server_info = first.json()["result"]["_meta"][
+            "io.modelcontextprotocol/serverInfo"
+        ]
+        assert discover_server_info["name"] == "doris-mcp-server"
+        assert discover_server_info["version"] == __version__
 
         unknown = await client.post(
             "/mcp",
@@ -440,6 +449,9 @@ async def 
test_http_rejects_untrusted_origin_and_legacy_is_stateless():
         assert initialized.status_code == 200
         assert "mcp-session-id" not in initialized.headers
         assert initialized.json()["result"]["protocolVersion"] == "2025-11-25"
+        legacy_server_info = initialized.json()["result"]["serverInfo"]
+        assert legacy_server_info["name"] == "doris-mcp-server"
+        assert legacy_server_info["version"] == __version__
 
 
 @pytest.mark.asyncio
diff --git a/test/protocol/test_multiworker_config.py 
b/test/protocol/test_multiworker_config.py
index 775b539..fdc5810 100644
--- a/test/protocol/test_multiworker_config.py
+++ b/test/protocol/test_multiworker_config.py
@@ -1,4 +1,10 @@
+import json
+
+import pytest
+
+from doris_mcp_server import __version__
 from doris_mcp_server.main import _multiworker_environment
+from doris_mcp_server.multiworker_app import health_check, root_info
 from doris_mcp_server.utils.config import DorisConfig
 
 
@@ -10,7 +16,6 @@ def 
test_multiworker_environment_preserves_resolved_parent_config(monkeypatch):
     config.database.password = "test-password"
     config.database.database = "hhm_dt_sim"
     config.server_name = "doris-mcp-server"
-    config.server_version = "0.6.1"
 
     worker_env = _multiworker_environment(
         config,
@@ -18,6 +23,8 @@ def 
test_multiworker_environment_preserves_resolved_parent_config(monkeypatch):
         port=31133,
         workers=2,
     )
+    assert "SERVER_VERSION" not in worker_env
+    monkeypatch.setenv("SERVER_VERSION", "9.9.9")
     for key, value in worker_env.items():
         monkeypatch.setenv(key, value)
 
@@ -30,6 +37,16 @@ def 
test_multiworker_environment_preserves_resolved_parent_config(monkeypatch):
     assert child_config.server_host == "127.0.0.1"
     assert child_config.server_port == 31133
     assert child_config.server_name == "doris-mcp-server"
-    assert child_config.server_version == "0.6.1"
+    assert child_config.server_version == __version__
     assert child_config.transport == "http"
     assert child_config.workers == 2
+
+
[email protected]
+async def test_multiworker_http_identity_reports_product_version():
+    for handler in (health_check, root_info):
+        response = await handler(None)
+        payload = json.loads(response.body)
+        assert payload["service"] == "doris-mcp-server"
+        assert payload["version"] == __version__
+        assert "mcp_version" not in payload
diff --git a/test/test_product_identity.py b/test/test_product_identity.py
new file mode 100644
index 0000000..2a8d8de
--- /dev/null
+++ b/test/test_product_identity.py
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Product identity must come from one build-time version source."""
+
+from importlib.metadata import version as distribution_version
+
+import pytest
+
+from doris_mcp_client.client import create_arg_parser as 
create_client_arg_parser
+from doris_mcp_server import __version__
+from doris_mcp_server.main import create_arg_parser as create_server_arg_parser
+from doris_mcp_server.utils.config import DorisConfig
+
+
+def test_package_metadata_matches_runtime_version():
+    assert distribution_version("doris-mcp-server") == __version__
+
+
[email protected](
+    "parser_factory",
+    [create_server_arg_parser, create_client_arg_parser],
+)
+def test_cli_reports_product_version(capsys, parser_factory):
+    with pytest.raises(SystemExit) as exit_info:
+        parser_factory().parse_args(["--version"])
+
+    assert exit_info.value.code == 0
+    assert capsys.readouterr().out.strip() == f"pytest {__version__}"
+
+
+def test_configuration_cannot_override_product_version(monkeypatch):
+    monkeypatch.setenv("SERVER_VERSION", "9.9.9")
+
+    assert DorisConfig.from_env().server_version == __version__
+    assert DorisConfig._from_dict({"server_version": "9.9.9"}).server_version 
== (
+        __version__
+    )
diff --git a/test/tools/test_tools_operation_guard.py 
b/test/tools/test_tools_operation_guard.py
index 1deedb7..447d77d 100644
--- a/test/tools/test_tools_operation_guard.py
+++ b/test/tools/test_tools_operation_guard.py
@@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock
 import mcp.types as mcp_types
 import pytest
 
+from doris_mcp_server import __version__
 from doris_mcp_server.auth.operation_policy import (
     HIGH_RISK_TOOLS,
     OperationAuthorizationError,
@@ -730,7 +731,7 @@ def _server_with_mock_managers():
         tools_manager=server.tools_manager,
         prompts_manager=server.prompts_manager,
         name="test-doris-mcp-server",
-        version="0.6.1",
+        version=__version__,
         logger=server.logger,
     )
     return server
diff --git a/uv.lock b/uv.lock
index 3c0ae53..ffc9188 100644
--- a/uv.lock
+++ b/uv.lock
@@ -591,7 +591,6 @@ wheels = [
 
 [[package]]
 name = "doris-mcp-server"
-version = "0.6.1"
 source = { editable = "." }
 dependencies = [
     { name = "adbc-driver-flightsql" },
@@ -756,7 +755,7 @@ requires-dist = [
     { name = "uvloop", marker = "extra == 'performance'", specifier = 
">=0.19.0" },
     { name = "websockets", specifier = ">=12.0" },
 ]
-provides-extras = ["dev", "docs", "performance", "monitoring"]
+provides-extras = ["dev", "docs", "monitoring", "performance"]
 
 [package.metadata.requires-dev]
 dev = [{ name = "ruff", specifier = ">=0.11.13" }]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to