From a89c37116689db73082901cba9351fa5879b79f8 Mon Sep 17 00:00:00 2001 From: "Turpaud, Remi" Date: Thu, 11 Sep 2025 11:40:44 +0200 Subject: [PATCH 1/2] fix f-string syntax errors, updated dev doc for wheel testing --- docs/developer_guide/DEVELOPER_GUIDE.md | 3 +-- src/teradata_mcp_server/app.py | 33 +++++++++++++++++++------ uv.lock | 2 +- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/docs/developer_guide/DEVELOPER_GUIDE.md b/docs/developer_guide/DEVELOPER_GUIDE.md index 29bb04e..51868ab 100644 --- a/docs/developer_guide/DEVELOPER_GUIDE.md +++ b/docs/developer_guide/DEVELOPER_GUIDE.md @@ -370,8 +370,7 @@ uv build --no-cache ### 2) Test the wheel locally (no install) ```bash # Run the installed console entry point from the wheel -uvx ./dist/teradata_mcp_server--py3-none-any.whl \ - python -m teradata_mcp_server --version +uvx ./dist/teradata_mcp_server--py3-none-any.whl teradata_mcp_server --version # Or install as a persistent tool and run uv tool install --reinstall ./dist/teradata_mcp_server--py3-none-any.whl diff --git a/src/teradata_mcp_server/app.py b/src/teradata_mcp_server/app.py index f70badc..4933a31 100644 --- a/src/teradata_mcp_server/app.py +++ b/src/teradata_mcp_server/app.py @@ -400,19 +400,25 @@ def _cube_query_tool(dimensions: str, measures: str, dim_filters: str, meas_filt expr = mdef["expression"] mes_lines.append(f"{expr} AS {measure}") meas_list = ",\n ".join(mes_lines) + top_clause = f"TOP {top}" if top else "" + dim_comma = ",\n " if dim_list.strip() else "" + where_dim_clause = f"WHERE {dim_filters}" if dim_filters else "" + where_meas_clause = f"WHERE {meas_filters}" if meas_filters else "" + order_clause = f"ORDER BY {order_by}" if order_by else "" + sql = ( - f"SELECT {'TOP ' + str(top) if top else ''} * from\n" + f"SELECT {top_clause} * from\n" "(SELECT\n" - f" {dim_list}{',\n ' if dim_list.strip() else ''}" + f" {dim_list}{dim_comma}" f" {meas_list}\n" "FROM (\n" f"{cube['sql'].strip()}\n" - f"{'WHERE '+dim_filters if dim_filters else ''}" + f"{where_dim_clause}" ") AS c\n" f"GROUP BY {', '.join(dim_list_raw)}" ") AS a\n" - f"{'WHERE '+meas_filters if meas_filters else ''}" - f"{'ORDER BY '+order_by if order_by else ''}" + f"{where_meas_clause}" + f"{order_clause}" ";" ) return sql @@ -439,6 +445,17 @@ async def _dynamic_tool(dimensions, measures, dim_filters="", meas_filters="", o measure_lines = [] for n, m in cube.get('measures', {}).items(): measure_lines.append(f" - {n}: {m.get('description', '')}") + + # Create example strings for documentation + dim_examples = [f"{d} {e}" for d, e in zip(list(cube.get('dimensions', {}))[:2], ["= 'value'", "in ('X', 'Y', 'Z')"])] + dim_example = ' AND '.join(dim_examples) + + meas_examples = [f"{m} {e}" for m, e in zip(list(cube.get('measures', {}))[:2], ["> 1000", "= 100"])] + meas_example = ' AND '.join(meas_examples) + + order_examples = [f"{d} {e}" for d, e in zip(list(cube.get('dimensions', {}))[:2], [" ASC", " DESC"])] + order_example = ' , '.join(order_examples) + _dynamic_tool.__doc__ = f""" Tool to query the cube '{name}'. {cube.get('description', '')} @@ -451,11 +468,11 @@ async def _dynamic_tool(dimensions, measures, dim_filters="", meas_filters="", o {chr(10).join(measure_lines)} * dim_filters (str): Filter expression to apply to dimensions. Valid dimension names are: [{', '.join(cube.get('dimensions', {}).keys())}], use valid SQL expressions, for example: - \"{' AND '.join([f"{d} {e}" for d, e in zip(list(cube.get('dimensions', {}))[:2], ["= 'value'", "in ('X', 'Y', 'Z')"])])}\" + "{dim_example}" * meas_filters (str): Filter expression to apply to computed measures. Valid measure names are: [{', '.join(cube.get('measures', {}).keys())}], use valid SQL expressions, for example: - \"{' AND '.join([f"{m} {e}" for m, e in zip(list(cube.get('measures', {}))[:2], ["> 1000", "= 100"])])}\" + "{meas_example}" * order_by (str): Order expression on any selected dimensions and measures. Use SQL syntax, for example: - \"{' , '.join([f"{d} {e}" for d, e in zip(list(cube.get('dimensions', {}))[:2], [" ASC", " DESC"])])}\" + "{order_example}" top (int): Limit the number of rows returned, use a positive integer. Returns: diff --git a/uv.lock b/uv.lock index a429004..3793d8e 100644 --- a/uv.lock +++ b/uv.lock @@ -2563,7 +2563,7 @@ wheels = [ [[package]] name = "teradata-mcp-server" -version = "0.1.3" +version = "0.1.4" source = { editable = "." } dependencies = [ { name = "fastmcp" }, From 9eff21a2c2f814af35744867ba7bad0d8c0822e6 Mon Sep 17 00:00:00 2001 From: "Turpaud, Remi" Date: Thu, 11 Sep 2025 14:41:12 +0200 Subject: [PATCH 2/2] updated dev doc, bumped version number --- README.md | 2 +- docs/developer_guide/DEVELOPER_GUIDE.md | 11 +++++++---- pyproject.toml | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1bdecee..7c921cd 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Configure the claude_desktop_config.json (Settings>Developer>Edit Config) by add "mcpServers": { "teradata": { "command": "uvx", - "args": ["teradata-mcp-server", "--profile", "all"], + "args": ["teradata-mcp-server"], "env": { "DATABASE_URI": "teradata://:@:1025/" } diff --git a/docs/developer_guide/DEVELOPER_GUIDE.md b/docs/developer_guide/DEVELOPER_GUIDE.md index 51868ab..a3ec185 100644 --- a/docs/developer_guide/DEVELOPER_GUIDE.md +++ b/docs/developer_guide/DEVELOPER_GUIDE.md @@ -355,6 +355,7 @@ mcp dev teradata-mcp-server ## Build, Test, and Publish We build with **uv**, test locally (wheel), then push to **TestPyPI** before PyPI. +The examples below use the Twine utility. ### Versions - The CLI reads its version from package metadata (`importlib.metadata`). @@ -379,13 +380,13 @@ uv tool install --reinstall ./dist/teradata_mcp_server--py3-none-any.whl ### 3) Verify metadata/README ```bash -twine check dist/* +uvx twine check dist/* ``` ### 4) Publish to **TestPyPI** (dress rehearsal) ```bash # Upload -python -m twine upload --repository testpypi dist/* +uvx twine upload --repository testpypi dist/* # Try installing the just-published version with uvx uvx --no-cache \ @@ -400,9 +401,11 @@ Notes: ### 5) Publish to **PyPI** ```bash -python -m twine upload dist/* +uvx twine upload dist/* ``` -If you see `File already exists`, bump the version in `pyproject.toml`, rebuild, and upload again. +If you see `File already exists`, it is either: +- You haven't bumped the the version in `pyproject.toml`. Do so, rebuild, and upload again. +- You have prior builds in the ./dist directory. Remove prior or be specify the exact version (eg. `uvx twine upload dist/*1.4.0*`) ### 6) Post‑publish smoke test ```bash diff --git a/pyproject.toml b/pyproject.toml index 79470c0..58a19df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "teradata-mcp-server" -version = "0.1.4" +version = "0.1.5" description = "Model Context Protocol (MCP) server for Teradata, Community edition" readme = {file = "README.md", content-type = "text/markdown"} license = {text = "MIT"}