This is an automated email from the ASF dual-hosted git repository.
FreeOnePlus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-mcp-server.git
The following commit(s) were added to refs/heads/master by this push:
new c90d8c3 fix: unify product version identity (#104)
c90d8c3 is described below
commit c90d8c32ba0a1364893ebdfc74bd6463d5a7b5e5
Author: Yijia Su <[email protected]>
AuthorDate: Wed Jul 29 18:53:55 2026 +0800
fix: unify product version identity (#104)
---
.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]