From 576652571c861d242d05bb3cf3d6963c30c99465 Mon Sep 17 00:00:00 2001 From: longhao Date: Tue, 27 May 2025 11:25:25 +0800 Subject: [PATCH] fix: correct FastMCP interface usage and resolve lint issues - Fix FastMCP server interface to use correct mcp.run() instead of app.run(host, port) - Remove unnecessary host and port parameters, use standard STDIO transport - Fix all ruff lint issues including import sorting and blank lines - Update ruff configuration to new lint configuration format - Fix type annotation issues using Union syntax (int | None) - Ensure uvx pypi-query-mcp-server works correctly - All tests pass and lint checks pass --- ...pi_query_mcp_server-0.1.0-py3-none-any.whl | Bin 17132 -> 17400 bytes dist/pypi_query_mcp_server-0.1.0.tar.gz | Bin 12586 -> 12932 bytes nox_actions/codetest.py | 4 +-- nox_actions/lint.py | 1 + nox_actions/release.py | 6 ++-- nox_actions/utils.py | 1 - noxfile.py | 8 ++--- pypi_query_mcp/__init__.py | 4 +-- pypi_query_mcp/core/exceptions.py | 8 ++--- pypi_query_mcp/server.py | 31 ++++++------------ pyproject.toml | 4 ++- tests/test_basic.py | 22 ++++++------- 12 files changed, 38 insertions(+), 51 deletions(-) diff --git a/dist/pypi_query_mcp_server-0.1.0-py3-none-any.whl b/dist/pypi_query_mcp_server-0.1.0-py3-none-any.whl index 379990fb9378eccea150807b65b256f4842445a1..2141a30ce29398973b508e62c65dcd483ece68d3 100644 GIT binary patch delta 5169 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 diff --git a/dist/pypi_query_mcp_server-0.1.0.tar.gz b/dist/pypi_query_mcp_server-0.1.0.tar.gz index 750c5aceda355ddcc0ab88b1e1808a0d3e5d492e..12f854e45675f7833de162d35e7da79515c8deed 100644 GIT binary patch literal 12932 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 z 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 M2VXdMCIA=#0Lqz`aR2}S diff --git a/nox_actions/codetest.py b/nox_actions/codetest.py index cf6b048..24f522a 100644 --- a/nox_actions/codetest.py +++ b/nox_actions/codetest.py @@ -3,8 +3,8 @@ import os # Import third-party modules import nox -from nox_actions.utils import PACKAGE_NAME -from nox_actions.utils import THIS_ROOT + +from nox_actions.utils import PACKAGE_NAME, THIS_ROOT def pytest(session: nox.Session) -> None: diff --git a/nox_actions/lint.py b/nox_actions/lint.py index 9b75bc1..bec2943 100644 --- a/nox_actions/lint.py +++ b/nox_actions/lint.py @@ -1,5 +1,6 @@ # Import third-party modules import nox + from nox_actions.utils import PACKAGE_NAME diff --git a/nox_actions/release.py b/nox_actions/release.py index b3b4a56..d736804 100644 --- a/nox_actions/release.py +++ b/nox_actions/release.py @@ -6,8 +6,8 @@ import zipfile # Import third-party modules import nox -from nox_actions.utils import PACKAGE_NAME -from nox_actions.utils import THIS_ROOT + +from nox_actions.utils import PACKAGE_NAME, THIS_ROOT def build(session: nox.Session) -> None: @@ -49,4 +49,4 @@ def build_exe(session: nox.Session) -> None: zip_obj.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), os.path.join(platform_dir, "."))) - print("Saving to {zipfile}".format(zipfile=zip_file)) + print(f"Saving to {zip_file}") diff --git a/nox_actions/utils.py b/nox_actions/utils.py index 076694e..a42eb1a 100644 --- a/nox_actions/utils.py +++ b/nox_actions/utils.py @@ -1,7 +1,6 @@ # Import built-in modules from pathlib import Path - PACKAGE_NAME = "pypi_query_mcp" THIS_ROOT = Path(__file__).parent.parent PROJECT_ROOT = THIS_ROOT.parent diff --git a/noxfile.py b/noxfile.py index 30b1182..d5d9a41 100644 --- a/noxfile.py +++ b/noxfile.py @@ -5,18 +5,14 @@ import sys # Import third-party modules import nox - ROOT = os.path.dirname(__file__) # Ensure pypi_query_mcp is importable. if ROOT not in sys.path: sys.path.append(ROOT) -# Import third-party modules -from nox_actions import codetest # noqa: E402 -from nox_actions import lint # noqa: E402 -from nox_actions import release # noqa: E402 - +# Import local modules (after sys.path setup) +from nox_actions import codetest, lint, release # noqa: E402 # Configure nox sessions nox.session(lint.lint, name="lint") diff --git a/pypi_query_mcp/__init__.py b/pypi_query_mcp/__init__.py index 0664362..1dca3d4 100644 --- a/pypi_query_mcp/__init__.py +++ b/pypi_query_mcp/__init__.py @@ -8,6 +8,6 @@ __version__ = "0.1.0" __author__ = "Hal" __email__ = "hal.long@outlook.com" -from pypi_query_mcp.server import app +from pypi_query_mcp.server import mcp -__all__ = ["app", "__version__"] +__all__ = ["mcp", "__version__"] diff --git a/pypi_query_mcp/core/exceptions.py b/pypi_query_mcp/core/exceptions.py index 6cdc942..239f2bc 100644 --- a/pypi_query_mcp/core/exceptions.py +++ b/pypi_query_mcp/core/exceptions.py @@ -4,7 +4,7 @@ class PyPIError(Exception): """Base exception for PyPI-related errors.""" - def __init__(self, message: str, status_code: int = None): + def __init__(self, message: str, status_code: int | None = None): super().__init__(message) self.message = message self.status_code = status_code @@ -22,7 +22,7 @@ class PackageNotFoundError(PyPIError): class NetworkError(PyPIError): """Raised when network-related errors occur.""" - def __init__(self, message: str, original_error: Exception = None): + def __init__(self, message: str, original_error: Exception | None = None): super().__init__(message) self.original_error = original_error @@ -30,7 +30,7 @@ class NetworkError(PyPIError): class RateLimitError(PyPIError): """Raised when API rate limit is exceeded.""" - def __init__(self, retry_after: int = None): + def __init__(self, retry_after: int | None = None): message = "PyPI API rate limit exceeded" if retry_after: message += f". Retry after {retry_after} seconds" @@ -50,7 +50,7 @@ class InvalidPackageNameError(PyPIError): class PyPIServerError(PyPIError): """Raised when PyPI server returns a server error.""" - def __init__(self, status_code: int, message: str = None): + def __init__(self, status_code: int, message: str | None = None): if not message: message = f"PyPI server error (HTTP {status_code})" super().__init__(message, status_code=status_code) diff --git a/pypi_query_mcp/server.py b/pypi_query_mcp/server.py index 7c97a90..c3a53c0 100644 --- a/pypi_query_mcp/server.py +++ b/pypi_query_mcp/server.py @@ -23,10 +23,10 @@ logging.basicConfig( logger = logging.getLogger(__name__) # Create FastMCP application -app = FastMCP("PyPI Query MCP Server") +mcp = FastMCP("PyPI Query MCP Server") -@app.tool() +@mcp.tool() async def get_package_info(package_name: str) -> dict[str, Any]: """Query comprehensive information about a PyPI package. @@ -71,7 +71,7 @@ async def get_package_info(package_name: str) -> dict[str, Any]: } -@app.tool() +@mcp.tool() async def get_package_versions(package_name: str) -> dict[str, Any]: """Get version information for a PyPI package. @@ -114,7 +114,7 @@ async def get_package_versions(package_name: str) -> dict[str, Any]: } -@app.tool() +@mcp.tool() async def get_package_dependencies(package_name: str, version: str | None = None) -> dict[str, Any]: """Get dependency information for a PyPI package. @@ -161,7 +161,7 @@ async def get_package_dependencies(package_name: str, version: str | None = None } -@app.tool() +@mcp.tool() async def check_package_python_compatibility( package_name: str, target_python_version: str, @@ -212,7 +212,7 @@ async def check_package_python_compatibility( } -@app.tool() +@mcp.tool() async def get_package_compatible_python_versions( package_name: str, python_versions: list[str] | None = None, @@ -262,33 +262,22 @@ async def get_package_compatible_python_versions( @click.command() -@click.option( - "--host", - default="localhost", - help="Host to bind the server to" -) -@click.option( - "--port", - default=8000, - type=int, - help="Port to bind the server to" -) @click.option( "--log-level", default="INFO", type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR"]), help="Logging level" ) -def main(host: str, port: int, log_level: str) -> None: +def main(log_level: str) -> None: """Start the PyPI Query MCP Server.""" # Set logging level logging.getLogger().setLevel(getattr(logging, log_level)) - logger.info(f"Starting PyPI Query MCP Server on {host}:{port}") + logger.info("Starting PyPI Query MCP Server") logger.info(f"Log level set to: {log_level}") - # Run the FastMCP server - app.run(host=host, port=port) + # Run the FastMCP server (uses STDIO transport by default) + mcp.run() if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 2e405f4..9b06b35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,8 @@ pypi-query-mcp = "pypi_query_mcp.server:main" [tool.ruff] target-version = "py310" line-length = 88 + +[tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings @@ -66,7 +68,7 @@ ignore = [ "C901", # too complex ] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] [tool.mypy] diff --git a/tests/test_basic.py b/tests/test_basic.py index 19210aa..8a392c4 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -13,22 +13,22 @@ def test_version(): def test_import(): """Test that main modules can be imported.""" from pypi_query_mcp.core import PyPIClient, VersionCompatibility - from pypi_query_mcp.server import app - + from pypi_query_mcp.server import mcp + assert PyPIClient is not None assert VersionCompatibility is not None - assert app is not None + assert mcp is not None @pytest.mark.asyncio async def test_pypi_client_basic(): """Test basic PyPI client functionality.""" from pypi_query_mcp.core import PyPIClient - + async with PyPIClient() as client: # Test that client can be created and closed assert client is not None - + # Test cache clearing client.clear_cache() @@ -36,24 +36,24 @@ async def test_pypi_client_basic(): def test_version_compatibility(): """Test version compatibility utility.""" from pypi_query_mcp.core import VersionCompatibility - + compat = VersionCompatibility() - + # Test requires_python parsing spec = compat.parse_requires_python(">=3.8") assert spec is not None - + # Test classifier extraction classifiers = [ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython" ] - + versions = compat.extract_python_versions_from_classifiers(classifiers) assert "3.8" in versions assert "3.9" in versions - + implementations = compat.extract_python_implementations(classifiers) assert "CPython" in implementations @@ -68,7 +68,7 @@ def test_mcp_tools_import(): query_package_versions, suggest_python_version_for_packages, ) - + # Test that all tools are callable assert callable(query_package_info) assert callable(query_package_versions)