|
| 1 | +[build-system] |
| 2 | +requires = ["setuptools>=61.0", "wheel"] |
| 3 | +build-backend = "setuptools.build_meta" |
| 4 | + |
| 5 | +[project] |
| 6 | +name = "ha-mcp" |
| 7 | +version = "7.4.1" |
| 8 | +description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP" |
| 9 | +readme = "README.md" |
| 10 | +requires-python = ">=3.13,<3.14" |
| 11 | +license = {text = "MIT"} |
| 12 | +authors = [ |
| 13 | + {name = "Julien", email = "github@qc-h.net"} |
| 14 | +] |
| 15 | +keywords = ["mcp", "home-assistant", "ai", "automation", "smart-home"] |
| 16 | +classifiers = [ |
| 17 | + "Development Status :: 5 - Production/Stable", |
| 18 | + "Intended Audience :: Developers", |
| 19 | + "License :: OSI Approved :: MIT License", |
| 20 | + "Programming Language :: Python :: 3", |
| 21 | + "Programming Language :: Python :: 3.13", |
| 22 | + "Topic :: Home Automation", |
| 23 | + "Topic :: Software Development :: Libraries :: Python Modules", |
| 24 | +] |
| 25 | + |
| 26 | +dependencies = [ |
| 27 | + "fastmcp==3.2.4", |
| 28 | + "httpx[socks]==0.28.1", |
| 29 | + "pydantic==2.13.3", |
| 30 | + "python-dotenv==1.2.2", |
| 31 | + "truststore==0.10.4", |
| 32 | + "websockets==16.0", |
| 33 | + "cryptography==47.0.0", |
| 34 | +] |
| 35 | + |
| 36 | +[project.urls] |
| 37 | +"Homepage" = "https://github.com/homeassistant-ai/ha-mcp" |
| 38 | +"Bug Tracker" = "https://github.com/homeassistant-ai/ha-mcp/issues" |
| 39 | +"Repository" = "https://github.com/homeassistant-ai/ha-mcp" |
| 40 | + |
| 41 | +[project.scripts] |
| 42 | +ha-mcp = "ha_mcp.__main__:main" |
| 43 | +ha-mcp-smoke-test = "ha_mcp.smoke_test:main" |
| 44 | +hamcp-test-env = "tests.test_env_manager:main" |
| 45 | +ha-mcp-web = "ha_mcp.__main__:main_web" |
| 46 | +ha-mcp-sse = "ha_mcp.__main__:main_sse" |
| 47 | +ha-mcp-oauth = "ha_mcp.__main__:main_oauth" |
| 48 | + |
| 49 | +[tool.setuptools] |
| 50 | +package-dir = {"" = "src", "tests" = "tests"} |
| 51 | +packages = { find = { where = ["src", "."], include = ["ha_mcp*", "tests"] } } |
| 52 | + |
| 53 | +[tool.setuptools.package-data] |
| 54 | +ha_mcp = ["py.typed", "_pypi_marker", "resources/skills-vendor/**/*"] |
| 55 | + |
| 56 | +[tool.mypy] |
| 57 | +python_version = "3.13" |
| 58 | +mypy_path = "src" |
| 59 | +# /tmp keeps the cache on Linux tmpfs — avoids the slow Windows FS in WSL2. |
| 60 | +# Windows-native contributors can remove this line or override with MYPY_CACHE_DIR. |
| 61 | +cache_dir = "/tmp/.mypy_cache" |
| 62 | +warn_return_any = true |
| 63 | +warn_unused_configs = true |
| 64 | +disallow_untyped_defs = true |
| 65 | +disallow_incomplete_defs = true |
| 66 | +check_untyped_defs = true |
| 67 | +no_implicit_optional = true |
| 68 | +warn_redundant_casts = true |
| 69 | +warn_unused_ignores = true |
| 70 | +show_error_codes = true |
| 71 | +namespace_packages = true |
| 72 | +explicit_package_bases = true |
| 73 | + |
| 74 | +[[tool.mypy.overrides]] |
| 75 | +module = [ |
| 76 | + "fastmcp.*", |
| 77 | + "jq", |
| 78 | +] |
| 79 | +ignore_missing_imports = true |
| 80 | + |
| 81 | +[[tool.mypy.overrides]] |
| 82 | +module = [ |
| 83 | + "homeassistant.*", |
| 84 | + "aiohttp", |
| 85 | + "voluptuous", |
| 86 | + "jsonschema", |
| 87 | +] |
| 88 | +ignore_missing_imports = true |
| 89 | + |
| 90 | + |
| 91 | +[tool.ruff] |
| 92 | +target-version = "py313" |
| 93 | +line-length = 88 |
| 94 | +extend-exclude = ["tests/initial_test_state"] |
| 95 | + |
| 96 | +[tool.ruff.lint] |
| 97 | +select = [ |
| 98 | + "E", # pycodestyle errors |
| 99 | + "W", # pycodestyle warnings |
| 100 | + "F", # pyflakes |
| 101 | + "I", # isort |
| 102 | + "B", # flake8-bugbear |
| 103 | + "C4", # flake8-comprehensions |
| 104 | + "UP", # pyupgrade |
| 105 | + "RUF", # ruff-specific rules |
| 106 | + "PIE", # misc lints |
| 107 | + "PERF", # performance anti-patterns |
| 108 | + "ASYNC", # async best practices |
| 109 | + "A", # builtin shadowing |
| 110 | + "LOG", # logging best practices |
| 111 | + "SIM", # simplify |
| 112 | +] |
| 113 | +ignore = [ |
| 114 | + "E501", # line too long — formatter handles this |
| 115 | + "B008", # function calls in defaults — FastMCP pattern |
| 116 | + "SIM102", # collapsible-if — sometimes less readable |
| 117 | + "SIM108", # ternary — sometimes less readable |
| 118 | + "SIM105", # contextlib.suppress — style preference |
| 119 | + "ASYNC109",# timeout params are standard in HA API patterns |
| 120 | + "RUF001", # ambiguous unicode — HA entity names use degree symbols etc |
| 121 | + "RUF003", # ambiguous unicode in comments |
| 122 | + "RUF010", # explicit f-string type conversion — str(x) is clearer than !s |
| 123 | + "F841", # unused variable — too many to fix now |
| 124 | + "RUF059", # unused unpacked variable |
| 125 | + "SIM118", # in-dict-keys — style preference |
| 126 | + "PIE810", # multiple-starts-ends-with — style preference |
| 127 | + "RUF005", # collection-literal-concatenation |
| 128 | + "RUF022", # unsorted-dunder-all |
| 129 | + "RUF013", # implicit-optional |
| 130 | +] |
| 131 | + |
| 132 | +[tool.ruff.lint.per-file-ignores] |
| 133 | +"__init__.py" = ["F401"] |
| 134 | +"tests/**/*" = ["E501", "B011"] |
| 135 | +# C901 ignores for tools files with complex methods (see #925). |
| 136 | +# Remove lines as individual methods are simplified below threshold. |
| 137 | +"src/ha_mcp/tools/tools_addons.py" = ["C901"] |
| 138 | +"src/ha_mcp/tools/tools_config_dashboards.py" = ["C901"] |
| 139 | +"src/ha_mcp/tools/tools_config_helpers.py" = ["C901"] |
| 140 | +"src/ha_mcp/tools/tools_entities.py" = ["C901"] |
| 141 | +"src/ha_mcp/tools/tools_registry.py" = ["C901"] |
| 142 | +"src/ha_mcp/tools/tools_search.py" = ["C901"] |
| 143 | +"src/ha_mcp/tools/tools_utility.py" = ["C901"] |
| 144 | +"src/ha_mcp/tools/smart_search.py" = ["C901"] |
| 145 | +"src/ha_mcp/tools/util_helpers.py" = ["C901"] |
| 146 | + |
| 147 | +[tool.pytest.ini_options] |
| 148 | +testpaths = ["tests"] |
| 149 | +python_files = ["test_*.py", "*_test.py"] |
| 150 | +python_classes = ["Test*"] |
| 151 | +python_functions = ["test_*"] |
| 152 | +addopts = [ |
| 153 | + "--strict-markers", |
| 154 | + "--strict-config", |
| 155 | + "--verbose", |
| 156 | + "--tb=short", |
| 157 | +] |
| 158 | +markers = [ |
| 159 | + "slow: marks tests as slow (deselect with '-m \"not slow\"')", |
| 160 | + "integration: marks tests as integration tests", |
| 161 | + "unit: marks tests as unit tests", |
| 162 | + "automation: automation lifecycle tests", |
| 163 | + "calendar: calendar event management tests", |
| 164 | + "device: device control tests", |
| 165 | + "script: script orchestration tests", |
| 166 | + "helper: helper integration tests", |
| 167 | + "convenience: convenience tools tests (scene, weather, energy, docs)", |
| 168 | + "error_handling: error handling and edge case tests", |
| 169 | + "cleanup: tests that create entities needing cleanup", |
| 170 | + "performance: performance measurement and regression tests", |
| 171 | +] |
| 172 | +asyncio_mode = "auto" |
| 173 | + |
| 174 | +[dependency-groups] |
| 175 | +dev = [ |
| 176 | + "build>=1.2.2", |
| 177 | + "docker>=7.1.0", |
| 178 | + "mypy>=1.17.0", |
| 179 | + "openai>=1.0.0", |
| 180 | + "psutil>=7.0.0", |
| 181 | + "pytest>=8.4.2", |
| 182 | + "pytest-asyncio>=1.1.0", |
| 183 | + "pytest-cov>=5.0.0", |
| 184 | + "pytest-xdist>=3.8.0", |
| 185 | + "requests>=2.25.0", |
| 186 | + "lefthook>=1.10.0", |
| 187 | + "ruamel.yaml>=0.18.0", |
| 188 | + "ruff>=0.12.12", |
| 189 | + "testcontainers>=4.13.0", |
| 190 | + "ast-grep-cli>=0.42.0", |
| 191 | +] |
| 192 | + |
| 193 | +# Semantic versioning configuration |
| 194 | +[tool.semantic_release] |
| 195 | +version_toml = ["pyproject.toml:project.version"] |
| 196 | +version_variables = [ |
| 197 | + "src/ha_mcp/__init__.py:__version__", |
| 198 | +] |
| 199 | +build_command = """ |
| 200 | +pip install uv && uv lock |
| 201 | +git add uv.lock |
| 202 | +""" |
| 203 | +dist_path = "dist/" |
| 204 | +upload_to_PyPI = true |
| 205 | +upload_to_release = true |
| 206 | +remove_dist = false |
| 207 | + |
| 208 | +# Commit message parsing |
| 209 | +commit_parser = "angular" |
| 210 | +commit_parser_options = { major_tags = ["BREAKING", "!"], minor_tags = ["feat"], patch_tags = ["fix", "perf", "refactor"] } |
| 211 | + |
| 212 | +# Changelog configuration |
| 213 | +[tool.semantic_release.changelog] |
| 214 | +template_dir = "templates" |
| 215 | +exclude_commit_patterns = [ |
| 216 | + "^.*\\(site\\).*$", # Commits with (site) scope |
| 217 | + '''chore\(addon\): [Ss]ync changelog.*''', # Changelog sync commits |
| 218 | +] |
| 219 | + |
| 220 | +[tool.semantic_release.changelog.default_templates] |
| 221 | +changelog_file = "CHANGELOG.md" |
| 222 | + |
| 223 | +# Version scheme |
| 224 | +major_on_zero = true |
| 225 | +allow_zero_version = false |
| 226 | + |
| 227 | +# Branch and tag configuration |
| 228 | +branch = "master" |
| 229 | + |
| 230 | +# Publish configuration (v10 syntax) |
| 231 | +[tool.semantic_release.publish] |
| 232 | +# GitHub release is created by build-binary.yml workflow to include binaries |
| 233 | +upload_to_vcs_release = false |
0 commit comments