[BUG] MCP server versions differing only in case collide on MySQL and SQL Server
[!WARNING] Before submitting a PR, please make sure that:
- A maintainer has triaged this issue and applied the
readylabel- This issue has no assignee
- No duplicate PR exists
PRs not meeting these requirements may be automatically closed.
Issues Policy acknowledgement
- I have read and agree to submit bug reports in accordance with the issues policy
Where did you encounter this bug?
Local machine
MLflow version
mlflow % uv run pip list | grep -i mlflow
clint 0.1.0
mlflow 3.15.3.dev0 System information
- OS Platform and Distribution (e.g., Linux Ubuntu 16.04): ProductName: macOS ProductVersion: 26.6.2 BuildVersion: 25G83
- Python version: Python 3.10.19
Describe the problem
MySQL and SQL Server give string columns a case-insensitive collation by default
(utf8mb4_0900_ai_ci and SQL_Latin1_General_CP1_CI_AS). mcp_server_versions.version
inherits this.
That column holds SemVer strings, and SemVer §11
compares prerelease identifiers as ASCII: 1.0.0-A and 1.0.0-a are two different
versions. Build metadata is likewise case-sensitive. Above databases treats them
as the same value.
Reproduce in MYSQL
Save this as repro.py in the repository root:
import uuid
from mlflow.entities.mcp_server import MCPStatus
from mlflow.environment_variables import MLFLOW_TRACKING_URI
from mlflow.exceptions import MlflowException
from mlflow.store.tracking.sqlalchemy_store import SqlAlchemyStore
store = SqlAlchemyStore(MLFLOW_TRACKING_URI.get(), "file:///tmp/artifacts")
server = f"io.github.test/case-{uuid.uuid4().hex[:8]}"
store.create_mcp_server(name=server)
# Two versions of one server, differing only in the case of the prerelease identifier.
try:
created = store.create_mcp_server_version(
{"name": server, "version": version, "title": "Test"}, status=MCPStatus.ACTIVE
)
print(f"create({version!r}) -> stored {created.version!r}")
except MlflowException as exc:
print(f"create({version!r}) -> {exc.error_code}")
got = store.get_mcp_server_version(name=server, version="1.0.0-a").version
print(f"get('1.0.0-a') -> {got!r}")
print("stored:", sorted(v.version for v in store.search_mcp_server_versions(name=server)))Then, from the repository root:
./tests/db/compose.sh run --rm mlflow-mysql python repro.pyOn MySQL:
create('1.0.0-A') -> stored '1.0.0-A'
create('1.0.0-a') -> RESOURCE_ALREADY_EXISTS
get('1.0.0-a') -> '1.0.0-A' <- wrong version returned
stored: ['1.0.0-A'] <- one version, two were registeredSQLite and PostgreSQL compare case-sensitively and thus behave correctly
Suggested fix
Pin a case-sensitive collation on the version columns, as the skill registry tables do in :
mlflow/mlflow/store/db_migrations/versions/mlflow/mlflow/store/tracking/dbmodels/models.py
# example
AGENT_PLUGIN_VERSION_STRING = (
String(128)
.with_variant(MYSQL_VARCHAR(128, collation="utf8mb4_bin"), "mysql")
.with_variant(MSSQL_VARCHAR(128, collation="SQL_Latin1_General_CP1_CS_AS"), "mssql")
)Every column holding an MCP version string must use the same type. MySQL rejects a
foreign key whose columns disagree on collation (error 3780), so changing only mcp_server_versions column and
not the others breaks the schema. The four columns are:
| Table | Column |
|---|---|
mcp_server_versions |
version |
mcp_server_version_tags |
version (FK → mcp_server_versions) |
mcp_server_aliases |
version |
mcp_access_endpoints |
server_version |
NOTE :
mcp_server_versions.version_prerelease_sort_keycolumn not require this as the encoding is digits only.mcp_server_versionshas shipped, so this would need an Alembic migration on a live database.
How to Test Fix
- Refer
tests/db/test_skill_registry_schema.py::test_db_backend_version_identity_is_case_sensitive. Runs across 4 db engines.
Willingness to contribute
Yes. I would be willing to contribute a fix for this bug with guidance from the MLflow community.
What component(s) does this bug affect?
-
area/tracking: Tracking Service, tracking client APIs, autologging -
area/model-registry: Model Registry service, APIs, and the fluent client calls for Model Registry -
area/scoring: MLflow model serving, deployment tools, Spark UDFs -
area/evaluation: MLflow model evaluation features, evaluation metrics, and evaluation workflows -
area/prompt: MLflow prompt engineering features, prompt templates, and prompt management -
area/tracing: MLflow Tracing features, tracing APIs, and LLM tracing functionality -
area/gateway: MLflow AI Gateway client APIs, server, and third-party integrations -
area/projects: MLproject format, project running backends -
area/uiux: Front-end, user experience, plotting -
area/docs: MLflow documentation pages
Source: mlflow/mlflow