[project] name = "mcphub-server" version = "3.7.0" description = "AI-native management hub for WordPress, WooCommerce, and self-hosted services via Model Context Protocol (MCP)" authors = [ {name = "MCP Hub", email = "contact@mcphub.dev"} ] readme = "README.md" license = "MIT" requires-python = ">=3.11" keywords = [ "mcp", "wordpress", "woocommerce", "ai", "self-hosted", "gitea", "n8n", "supabase", "appwrite", "directus", "model-context-protocol", "claude", "automation", ] classifiers = [ "Development Status :: 4 - Beta", "Operating System :: OS Independent", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Systems Administration", "Topic :: Internet :: WWW/HTTP :: Site Management", ] dependencies = [ "fastmcp>=3.0.0,<4.0.0", "httpx>=0.25.0", "aiohttp>=3.9.0", "pydantic>=2.5.0", "python-dotenv>=1.0.0", "docker>=7.0.0", "authlib>=1.5.0", "PyJWT>=2.8.0", "cryptography>=46.0.0", "aiosqlite>=0.20.0", "jinja2>=3.1.2", "bcrypt>=4.0.0", ] [project.optional-dependencies] dev = [ "pytest>=7.4.0", "pytest-asyncio>=0.21.0", "pytest-cov>=4.1.0", "pytest-mock>=3.11.0", "black>=24.1.0", "ruff>=0.1.0", "mypy>=1.7.0", "pre-commit>=3.5.0", ] [project.scripts] mcphub = "server:main" [project.urls] Homepage = "https://github.com/airano-ir/mcphub" Repository = "https://github.com/airano-ir/mcphub" Documentation = "https://github.com/airano-ir/mcphub#readme" Issues = "https://github.com/airano-ir/mcphub/issues" Changelog = "https://github.com/airano-ir/mcphub/releases" [build-system] requires = ["setuptools>=68.0", "wheel"] build-backend = "setuptools.build_meta" [tool.setuptools] py-modules = ["server"] [tool.setuptools.packages.find] include = ["core*", "plugins*"] exclude = ["tests*", "data*", "logs*", "temp*", "scripts*", "docs*"] [tool.setuptools.package-data] "core" = ["templates/**/*.html"] "*" = ["*.html", "*.css", "*.js", "*.json"] # ==================================== # pytest Configuration # ==================================== [tool.pytest.ini_options] minversion = "7.0" testpaths = ["tests"] python_files = ["test_*.py"] python_classes = ["Test*"] python_functions = ["test_*"] addopts = [ "-ra", # Show summary of all test outcomes "-q", # Decrease verbosity "--strict-markers", # Enforce marker registration "--strict-config", # Enforce configuration "--tb=short", # Short traceback format "--ignore=tests/legacy", # Exclude legacy standalone scripts "--ignore=tests/run_integration_tests.py", # Exclude integration runner ] markers = [ "slow: marks tests as slow (deselect with '-m \"not slow\"')", "integration: marks tests as integration tests", "unit: marks tests as unit tests", "security: marks tests as security tests", ] asyncio_mode = "auto" # Automatically detect async tests # ==================================== # Coverage Configuration # ==================================== [tool.coverage.run] source = ["core", "plugins"] omit = [ "*/tests/*", "*/test_*.py", "*/__pycache__/*", "*/venv/*", "*/env/*", ] branch = true # Measure branch coverage [tool.coverage.report] precision = 2 show_missing = true skip_covered = false exclude_lines = [ "pragma: no cover", "def __repr__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "class .*\\bProtocol\\):", "@(abc\\.)?abstractmethod", ] [tool.coverage.html] directory = "htmlcov" [tool.coverage.xml] output = "coverage.xml" # Target coverage [tool.coverage.paths] source = ["core", "plugins"] # ==================================== # Black Configuration (Formatting) # ==================================== [tool.black] line-length = 100 target-version = ["py311"] include = '\.pyi?$' exclude = ''' /( \.git | \.venv | venv | \.pytest_cache | \.mypy_cache | \.ruff_cache | __pycache__ | build | dist )/ ''' # ==================================== # Ruff Configuration (Linting) # ==================================== [tool.ruff] line-length = 100 target-version = "py311" src = ["core", "plugins", "tests"] [tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "I", # isort "N", # pep8-naming "UP", # pyupgrade "B", # flake8-bugbear "C4", # flake8-comprehensions "SIM", # flake8-simplify ] ignore = [ "E501", # Line too long (handled by black) "E722", # Bare except (TODO: add specific exception types) "N802", # Function name should be lowercase (WordPress API naming) "N803", # Argument name should be lowercase "N806", # Variable in function should be lowercase "N805", # First argument of a method should be named 'self' "B904", # raise without from in except (TODO: fix incrementally) "SIM102", # Collapsible if (readability preference) "SIM105", # Use contextlib.suppress (readability preference) "SIM108", # Use ternary operator (readability preference) "SIM117", # Multiple with statements (readability preference) "SIM118", # Use key in dict (some false positives with custom objects) ] fixable = ["ALL"] unfixable = [] [tool.ruff.lint.per-file-ignores] "tests/*" = ["S101", "B007"] "tests/legacy/*" = ["E402", "B007", "F401"] "server.py" = ["E402", "F405", "F403", "UP045", "B023"] # Late imports + star imports + dynamic types "core/__init__.py" = ["F401", "F403"] # Re-exports "plugins/__init__.py" = ["F401"] "plugins/*/__init__.py" = ["F401", "F403", "F405"] [tool.ruff.lint.isort] known-first-party = ["core", "plugins"] # ==================================== # mypy Configuration (Type Checking) # ==================================== [tool.mypy] python_version = "3.11" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = false # Allow untyped defs for now disallow_incomplete_defs = false check_untyped_defs = true no_implicit_optional = true warn_redundant_casts = true warn_unused_ignores = true warn_no_return = true warn_unreachable = true strict_equality = true [[tool.mypy.overrides]] module = "tests.*" disallow_untyped_defs = false [[tool.mypy.overrides]] module = "fastmcp.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "httpx.*" ignore_missing_imports = true # ==================================== # Bandit Configuration (Security) # ==================================== [tool.bandit] exclude_dirs = ["/tests", "/examples", "/venv", "/env"] skips = ["B101", "B601"] # Skip assert_used and paramiko_calls # ==================================== # isort Configuration (Import Sorting) # ==================================== [tool.isort] profile = "black" line_length = 100 skip_gitignore = true known_first_party = ["src"] src_paths = ["src", "tests"]