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
This commit is contained in:
longhao 2025-05-27 11:25:25 +08:00 committed by Hal
parent 030b3a2607
commit 576652571c
12 changed files with 38 additions and 51 deletions

Binary file not shown.

Binary file not shown.

View File

@ -3,8 +3,8 @@ import os
# Import third-party modules # Import third-party modules
import nox 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: def pytest(session: nox.Session) -> None:

View File

@ -1,5 +1,6 @@
# Import third-party modules # Import third-party modules
import nox import nox
from nox_actions.utils import PACKAGE_NAME from nox_actions.utils import PACKAGE_NAME

View File

@ -6,8 +6,8 @@ import zipfile
# Import third-party modules # Import third-party modules
import nox 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: 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), zip_obj.write(os.path.join(root, file),
os.path.relpath(os.path.join(root, file), os.path.relpath(os.path.join(root, file),
os.path.join(platform_dir, "."))) os.path.join(platform_dir, ".")))
print("Saving to {zipfile}".format(zipfile=zip_file)) print(f"Saving to {zip_file}")

View File

@ -1,7 +1,6 @@
# Import built-in modules # Import built-in modules
from pathlib import Path from pathlib import Path
PACKAGE_NAME = "pypi_query_mcp" PACKAGE_NAME = "pypi_query_mcp"
THIS_ROOT = Path(__file__).parent.parent THIS_ROOT = Path(__file__).parent.parent
PROJECT_ROOT = THIS_ROOT.parent PROJECT_ROOT = THIS_ROOT.parent

View File

@ -5,18 +5,14 @@ import sys
# Import third-party modules # Import third-party modules
import nox import nox
ROOT = os.path.dirname(__file__) ROOT = os.path.dirname(__file__)
# Ensure pypi_query_mcp is importable. # Ensure pypi_query_mcp is importable.
if ROOT not in sys.path: if ROOT not in sys.path:
sys.path.append(ROOT) sys.path.append(ROOT)
# Import third-party modules # Import local modules (after sys.path setup)
from nox_actions import codetest # noqa: E402 from nox_actions import codetest, lint, release # noqa: E402
from nox_actions import lint # noqa: E402
from nox_actions import release # noqa: E402
# Configure nox sessions # Configure nox sessions
nox.session(lint.lint, name="lint") nox.session(lint.lint, name="lint")

View File

@ -8,6 +8,6 @@ __version__ = "0.1.0"
__author__ = "Hal" __author__ = "Hal"
__email__ = "hal.long@outlook.com" __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__"]

View File

@ -4,7 +4,7 @@
class PyPIError(Exception): class PyPIError(Exception):
"""Base exception for PyPI-related errors.""" """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) super().__init__(message)
self.message = message self.message = message
self.status_code = status_code self.status_code = status_code
@ -22,7 +22,7 @@ class PackageNotFoundError(PyPIError):
class NetworkError(PyPIError): class NetworkError(PyPIError):
"""Raised when network-related errors occur.""" """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) super().__init__(message)
self.original_error = original_error self.original_error = original_error
@ -30,7 +30,7 @@ class NetworkError(PyPIError):
class RateLimitError(PyPIError): class RateLimitError(PyPIError):
"""Raised when API rate limit is exceeded.""" """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" message = "PyPI API rate limit exceeded"
if retry_after: if retry_after:
message += f". Retry after {retry_after} seconds" message += f". Retry after {retry_after} seconds"
@ -50,7 +50,7 @@ class InvalidPackageNameError(PyPIError):
class PyPIServerError(PyPIError): class PyPIServerError(PyPIError):
"""Raised when PyPI server returns a server error.""" """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: if not message:
message = f"PyPI server error (HTTP {status_code})" message = f"PyPI server error (HTTP {status_code})"
super().__init__(message, status_code=status_code) super().__init__(message, status_code=status_code)

View File

@ -23,10 +23,10 @@ logging.basicConfig(
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Create FastMCP application # 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]: async def get_package_info(package_name: str) -> dict[str, Any]:
"""Query comprehensive information about a PyPI package. """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]: async def get_package_versions(package_name: str) -> dict[str, Any]:
"""Get version information for a PyPI package. """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]: async def get_package_dependencies(package_name: str, version: str | None = None) -> dict[str, Any]:
"""Get dependency information for a PyPI package. """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( async def check_package_python_compatibility(
package_name: str, package_name: str,
target_python_version: 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( async def get_package_compatible_python_versions(
package_name: str, package_name: str,
python_versions: list[str] | None = None, python_versions: list[str] | None = None,
@ -262,33 +262,22 @@ async def get_package_compatible_python_versions(
@click.command() @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( @click.option(
"--log-level", "--log-level",
default="INFO", default="INFO",
type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR"]), type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR"]),
help="Logging level" 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.""" """Start the PyPI Query MCP Server."""
# Set logging level # Set logging level
logging.getLogger().setLevel(getattr(logging, log_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}") logger.info(f"Log level set to: {log_level}")
# Run the FastMCP server # Run the FastMCP server (uses STDIO transport by default)
app.run(host=host, port=port) mcp.run()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -51,6 +51,8 @@ pypi-query-mcp = "pypi_query_mcp.server:main"
[tool.ruff] [tool.ruff]
target-version = "py310" target-version = "py310"
line-length = 88 line-length = 88
[tool.ruff.lint]
select = [ select = [
"E", # pycodestyle errors "E", # pycodestyle errors
"W", # pycodestyle warnings "W", # pycodestyle warnings
@ -66,7 +68,7 @@ ignore = [
"C901", # too complex "C901", # too complex
] ]
[tool.ruff.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] "__init__.py" = ["F401"]
[tool.mypy] [tool.mypy]

View File

@ -13,11 +13,11 @@ def test_version():
def test_import(): def test_import():
"""Test that main modules can be imported.""" """Test that main modules can be imported."""
from pypi_query_mcp.core import PyPIClient, VersionCompatibility 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 PyPIClient is not None
assert VersionCompatibility is not None assert VersionCompatibility is not None
assert app is not None assert mcp is not None
@pytest.mark.asyncio @pytest.mark.asyncio