ha-mcp-dev 6.3.1.dev137__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. ha_mcp/__init__.py +51 -0
  2. ha_mcp/__main__.py +753 -0
  3. ha_mcp/_pypi_marker +0 -0
  4. ha_mcp/auth/__init__.py +10 -0
  5. ha_mcp/auth/consent_form.py +501 -0
  6. ha_mcp/auth/provider.py +786 -0
  7. ha_mcp/client/__init__.py +10 -0
  8. ha_mcp/client/rest_client.py +924 -0
  9. ha_mcp/client/websocket_client.py +656 -0
  10. ha_mcp/client/websocket_listener.py +340 -0
  11. ha_mcp/config.py +175 -0
  12. ha_mcp/errors.py +399 -0
  13. ha_mcp/py.typed +0 -0
  14. ha_mcp/resources/card_types.json +48 -0
  15. ha_mcp/resources/dashboard_guide.md +573 -0
  16. ha_mcp/server.py +189 -0
  17. ha_mcp/smoke_test.py +108 -0
  18. ha_mcp/tools/__init__.py +11 -0
  19. ha_mcp/tools/backup.py +457 -0
  20. ha_mcp/tools/device_control.py +687 -0
  21. ha_mcp/tools/enhanced.py +191 -0
  22. ha_mcp/tools/helpers.py +184 -0
  23. ha_mcp/tools/registry.py +199 -0
  24. ha_mcp/tools/smart_search.py +797 -0
  25. ha_mcp/tools/tools_addons.py +343 -0
  26. ha_mcp/tools/tools_areas.py +478 -0
  27. ha_mcp/tools/tools_blueprints.py +279 -0
  28. ha_mcp/tools/tools_bug_report.py +570 -0
  29. ha_mcp/tools/tools_calendar.py +391 -0
  30. ha_mcp/tools/tools_camera.py +149 -0
  31. ha_mcp/tools/tools_config_automations.py +508 -0
  32. ha_mcp/tools/tools_config_dashboards.py +1502 -0
  33. ha_mcp/tools/tools_config_entry_flow.py +223 -0
  34. ha_mcp/tools/tools_config_helpers.py +925 -0
  35. ha_mcp/tools/tools_config_info.py +258 -0
  36. ha_mcp/tools/tools_config_scripts.py +267 -0
  37. ha_mcp/tools/tools_entities.py +76 -0
  38. ha_mcp/tools/tools_filesystem.py +589 -0
  39. ha_mcp/tools/tools_groups.py +306 -0
  40. ha_mcp/tools/tools_hacs.py +787 -0
  41. ha_mcp/tools/tools_history.py +714 -0
  42. ha_mcp/tools/tools_integrations.py +297 -0
  43. ha_mcp/tools/tools_labels.py +713 -0
  44. ha_mcp/tools/tools_mcp_component.py +305 -0
  45. ha_mcp/tools/tools_registry.py +1115 -0
  46. ha_mcp/tools/tools_resources.py +622 -0
  47. ha_mcp/tools/tools_search.py +573 -0
  48. ha_mcp/tools/tools_service.py +211 -0
  49. ha_mcp/tools/tools_services.py +352 -0
  50. ha_mcp/tools/tools_system.py +363 -0
  51. ha_mcp/tools/tools_todo.py +530 -0
  52. ha_mcp/tools/tools_traces.py +496 -0
  53. ha_mcp/tools/tools_updates.py +476 -0
  54. ha_mcp/tools/tools_utility.py +519 -0
  55. ha_mcp/tools/tools_voice_assistant.py +345 -0
  56. ha_mcp/tools/tools_zones.py +387 -0
  57. ha_mcp/tools/util_helpers.py +199 -0
  58. ha_mcp/utils/__init__.py +30 -0
  59. ha_mcp/utils/domain_handlers.py +380 -0
  60. ha_mcp/utils/fuzzy_search.py +339 -0
  61. ha_mcp/utils/operation_manager.py +442 -0
  62. ha_mcp/utils/usage_logger.py +290 -0
  63. ha_mcp_dev-6.3.1.dev137.dist-info/METADATA +229 -0
  64. ha_mcp_dev-6.3.1.dev137.dist-info/RECORD +71 -0
  65. ha_mcp_dev-6.3.1.dev137.dist-info/WHEEL +5 -0
  66. ha_mcp_dev-6.3.1.dev137.dist-info/entry_points.txt +7 -0
  67. ha_mcp_dev-6.3.1.dev137.dist-info/licenses/LICENSE +21 -0
  68. ha_mcp_dev-6.3.1.dev137.dist-info/top_level.txt +2 -0
  69. tests/__init__.py +1 -0
  70. tests/test_constants.py +15 -0
  71. tests/test_env_manager.py +336 -0
@@ -0,0 +1,290 @@
1
+ """
2
+ Usage logging for MCP tool calls to track usage patterns and performance metrics.
3
+ """
4
+
5
+ import json
6
+ import logging
7
+ import threading
8
+ import time
9
+ from collections import deque
10
+ from dataclasses import asdict, dataclass
11
+ from datetime import UTC, datetime
12
+ from pathlib import Path
13
+ from queue import Queue
14
+ from typing import Any
15
+
16
+
17
+ # Default ring buffer size - keeps last N entries in memory
18
+ DEFAULT_RING_BUFFER_SIZE = 200
19
+
20
+ # Average log entries per tool call (empirically observed)
21
+ # This includes the tool itself plus any associated operations
22
+ AVG_LOG_ENTRIES_PER_TOOL = 3
23
+
24
+ # Startup log collection duration in seconds
25
+ STARTUP_LOG_DURATION_SECONDS = 60
26
+
27
+
28
+ class StartupLogCollector(logging.Handler):
29
+ """Collects log messages during the first minute of server startup."""
30
+
31
+ def __init__(self, duration_seconds: int = STARTUP_LOG_DURATION_SECONDS):
32
+ super().__init__()
33
+ self._start_time = time.time()
34
+ self._duration = duration_seconds
35
+ self._logs: list[dict[str, Any]] = []
36
+ self._lock = threading.Lock()
37
+ self._active = True
38
+
39
+ def emit(self, record: logging.LogRecord) -> None:
40
+ """Capture log record if within startup window."""
41
+ if not self._active:
42
+ return
43
+
44
+ elapsed = time.time() - self._start_time
45
+ if elapsed > self._duration:
46
+ self._active = False
47
+ return
48
+
49
+ with self._lock:
50
+ self._logs.append({
51
+ "timestamp": datetime.now(UTC).isoformat(),
52
+ "level": record.levelname,
53
+ "logger": record.name,
54
+ "message": record.getMessage(),
55
+ "elapsed_seconds": round(elapsed, 2),
56
+ })
57
+
58
+ def get_logs(self) -> list[dict[str, Any]]:
59
+ """Get collected startup logs."""
60
+ with self._lock:
61
+ return list(self._logs)
62
+
63
+ def is_active(self) -> bool:
64
+ """Check if still collecting startup logs."""
65
+ return self._active and (time.time() - self._start_time) <= self._duration
66
+
67
+
68
+ # Global startup log collector - initialized at module import
69
+ _startup_collector: StartupLogCollector | None = None
70
+
71
+
72
+ def _init_startup_collector() -> None:
73
+ """Initialize startup log collector and attach to root logger."""
74
+ global _startup_collector
75
+ if _startup_collector is None:
76
+ _startup_collector = StartupLogCollector()
77
+ _startup_collector.setLevel(logging.DEBUG)
78
+ logging.getLogger().addHandler(_startup_collector)
79
+
80
+
81
+ # Initialize on module load
82
+ _init_startup_collector()
83
+
84
+
85
+ def get_startup_logs() -> list[dict[str, Any]]:
86
+ """Get startup logs collected during the first minute."""
87
+ if _startup_collector is None:
88
+ return []
89
+ return _startup_collector.get_logs()
90
+
91
+
92
+ @dataclass
93
+ class ToolUsageLog:
94
+ """Single tool usage log entry."""
95
+
96
+ timestamp: str
97
+ tool_name: str
98
+ parameters: dict[str, Any]
99
+ execution_time_ms: float
100
+ success: bool
101
+ error_message: str | None = None
102
+ response_size_bytes: int | None = None
103
+ user_context: str | None = None
104
+
105
+
106
+ class UsageLogger:
107
+ """Async disk logger for MCP tool usage tracking with in-memory ring buffer."""
108
+
109
+ def __init__(
110
+ self,
111
+ log_file_path: str | None = None,
112
+ ring_buffer_size: int = DEFAULT_RING_BUFFER_SIZE,
113
+ ):
114
+ self._enabled = True
115
+
116
+ if log_file_path:
117
+ self.log_file_path = Path(log_file_path)
118
+ else:
119
+ # Use user's home directory by default to avoid read-only filesystem errors
120
+ # when running via uvx/npx which might have read-only CWD
121
+ self.log_file_path = Path.home() / ".ha-mcp" / "logs" / "mcp_usage.jsonl"
122
+
123
+ try:
124
+ self.log_file_path.parent.mkdir(parents=True, exist_ok=True)
125
+ except OSError:
126
+ # Directory creation failed (e.g., read-only filesystem)
127
+ # Disable logging silently to avoid disrupting the MCP server
128
+ self._enabled = False
129
+
130
+ # In-memory ring buffer for fast access to recent logs
131
+ # Thread-safe: deque is thread-safe for append/pop operations
132
+ self._ring_buffer: deque[dict[str, Any]] = deque(maxlen=ring_buffer_size)
133
+ self._buffer_lock = threading.Lock()
134
+
135
+ # Thread-safe queue for disk writes
136
+ self._log_queue: Queue[ToolUsageLog] = Queue()
137
+ self._logger_thread: threading.Thread | None = None
138
+ self._stop_event = threading.Event()
139
+ if self._enabled:
140
+ self._start_logger_thread()
141
+
142
+ def _start_logger_thread(self) -> None:
143
+ """Start background thread for disk writes."""
144
+ self._logger_thread = threading.Thread(
145
+ target=self._log_writer_worker, daemon=True
146
+ )
147
+ self._logger_thread.start()
148
+
149
+ def _log_writer_worker(self) -> None:
150
+ """Background thread worker for writing logs to disk."""
151
+ while not self._stop_event.is_set():
152
+ try:
153
+ # Wait for log entry with timeout to check stop event
154
+ if not self._log_queue.empty():
155
+ log_entry = self._log_queue.get(timeout=1.0)
156
+ self._write_log_entry(log_entry)
157
+ else:
158
+ # Sleep briefly to avoid busy waiting
159
+ threading.Event().wait(0.1)
160
+ except Exception as e:
161
+ # Silent error handling to avoid disrupting MCP server
162
+ print(f"Usage logger error: {e}")
163
+
164
+ def _write_log_entry(self, log_entry: ToolUsageLog) -> None:
165
+ """Write single log entry to disk."""
166
+ try:
167
+ with open(self.log_file_path, "a", encoding="utf-8") as f:
168
+ json.dump(asdict(log_entry), f, ensure_ascii=False)
169
+ f.write("\n")
170
+ except Exception:
171
+ # Silent error handling
172
+ pass
173
+
174
+ def log_tool_usage(
175
+ self,
176
+ tool_name: str,
177
+ parameters: dict[str, Any],
178
+ execution_time_ms: float,
179
+ success: bool,
180
+ error_message: str | None = None,
181
+ response_size_bytes: int | None = None,
182
+ user_context: str | None = None,
183
+ ) -> None:
184
+ """Log tool usage (non-blocking)."""
185
+ if not self._enabled:
186
+ return
187
+ try:
188
+ log_entry = ToolUsageLog(
189
+ timestamp=datetime.now(UTC).isoformat(),
190
+ tool_name=tool_name,
191
+ parameters=parameters,
192
+ execution_time_ms=execution_time_ms,
193
+ success=success,
194
+ error_message=error_message,
195
+ response_size_bytes=response_size_bytes,
196
+ user_context=user_context,
197
+ )
198
+
199
+ # Add to ring buffer (thread-safe)
200
+ entry_dict = asdict(log_entry)
201
+ with self._buffer_lock:
202
+ self._ring_buffer.append(entry_dict)
203
+
204
+ # Queue for disk write
205
+ self._log_queue.put(log_entry)
206
+ except Exception:
207
+ # Silent error handling to never break MCP server
208
+ pass
209
+
210
+ def get_recent_entries(self, count: int) -> list[dict[str, Any]]:
211
+ """
212
+ Get recent log entries from the in-memory ring buffer.
213
+
214
+ Args:
215
+ count: Maximum number of entries to return
216
+
217
+ Returns:
218
+ List of log entries as dictionaries, ordered newest to oldest
219
+ """
220
+ with self._buffer_lock:
221
+ # Get the last N entries, reversed to newest-first order
222
+ buffer_list = list(self._ring_buffer)
223
+ return list(reversed(buffer_list[-count:]))
224
+
225
+ def shutdown(self) -> None:
226
+ """Gracefully shutdown logger."""
227
+ self._stop_event.set()
228
+ if self._logger_thread and self._logger_thread.is_alive():
229
+ self._logger_thread.join(timeout=2.0)
230
+
231
+
232
+ # Global logger instance
233
+ _usage_logger: UsageLogger | None = None
234
+
235
+
236
+ def get_usage_logger() -> UsageLogger:
237
+ """Get global usage logger instance."""
238
+ global _usage_logger
239
+ if _usage_logger is None:
240
+ _usage_logger = UsageLogger()
241
+ return _usage_logger
242
+
243
+
244
+ def log_tool_call(
245
+ tool_name: str,
246
+ parameters: dict[str, Any],
247
+ execution_time_ms: float,
248
+ success: bool,
249
+ error_message: str | None = None,
250
+ response_size_bytes: int | None = None,
251
+ user_context: str | None = None,
252
+ ) -> None:
253
+ """Convenience function to log tool usage."""
254
+ logger = get_usage_logger()
255
+ logger.log_tool_usage(
256
+ tool_name=tool_name,
257
+ parameters=parameters,
258
+ execution_time_ms=execution_time_ms,
259
+ success=success,
260
+ error_message=error_message,
261
+ response_size_bytes=response_size_bytes,
262
+ user_context=user_context,
263
+ )
264
+
265
+
266
+ def shutdown_usage_logger() -> None:
267
+ """Shutdown global usage logger."""
268
+ global _usage_logger
269
+ if _usage_logger:
270
+ _usage_logger.shutdown()
271
+ _usage_logger = None
272
+
273
+
274
+ def get_recent_logs(max_entries: int = 20) -> list[dict[str, Any]]:
275
+ """
276
+ Get recent log entries from the in-memory ring buffer.
277
+
278
+ This is O(1) access - no file I/O required.
279
+
280
+ Args:
281
+ max_entries: Maximum number of log entries to return (most recent first)
282
+
283
+ Returns:
284
+ List of log entries as dictionaries, ordered newest to oldest
285
+ """
286
+ logger = get_usage_logger()
287
+ if not logger._enabled:
288
+ return []
289
+
290
+ return logger.get_recent_entries(max_entries)
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.4
2
+ Name: ha-mcp-dev
3
+ Version: 6.3.1.dev137
4
+ Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
+ Author-email: Julien <github@qc-h.net>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/homeassistant-ai/ha-mcp
8
+ Project-URL: Bug Tracker, https://github.com/homeassistant-ai/ha-mcp/issues
9
+ Project-URL: Repository, https://github.com/homeassistant-ai/ha-mcp
10
+ Keywords: mcp,home-assistant,ai,automation,smart-home
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Home Automation
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: <3.14,>=3.13
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: fastmcp>=2.11.0
22
+ Requires-Dist: httpx[socks]<1.0,>=0.27.0
23
+ Requires-Dist: jq>=1.8.0; sys_platform != "win32"
24
+ Requires-Dist: pydantic>=2.5.0
25
+ Requires-Dist: python-dotenv>=1.0.0
26
+ Requires-Dist: truststore>=0.10.0
27
+ Requires-Dist: websockets>=12.0
28
+ Requires-Dist: cryptography>=45.0.7
29
+ Dynamic: license-file
30
+
31
+ <div align="center">
32
+ <img src="docs/img/ha-mcp-logo.png" alt="Home Assistant MCP Server Logo" width="300"/>
33
+
34
+ # The Unofficial and Awesome Home Assistant MCP Server
35
+
36
+ <!-- mcp-name: io.github.homeassistant-ai/ha-mcp -->
37
+
38
+ <p align="center">
39
+ <img src="https://img.shields.io/badge/tools-95+-blue" alt="95+ Tools">
40
+ <a href="https://github.com/homeassistant-ai/ha-mcp/releases"><img src="https://img.shields.io/github/v/release/homeassistant-ai/ha-mcp" alt="Release"></a>
41
+ <a href="https://github.com/homeassistant-ai/ha-mcp/actions/workflows/e2e-tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/homeassistant-ai/ha-mcp/e2e-tests.yml?branch=master&label=E2E%20Tests" alt="E2E Tests"></a>
42
+ <a href="LICENSE.md"><img src="https://img.shields.io/github/license/homeassistant-ai/ha-mcp.svg" alt="License"></a>
43
+ <br>
44
+ <a href="https://github.com/homeassistant-ai/ha-mcp/commits/master"><img src="https://img.shields.io/github/commit-activity/m/homeassistant-ai/ha-mcp.svg" alt="Activity"></a>
45
+ <a href="https://github.com/jlowin/fastmcp"><img src="https://img.shields.io/badge/Built%20with-FastMCP-purple" alt="Built with FastMCP"></a>
46
+ <img src="https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fhomeassistant-ai%2Fha-mcp%2Fmaster%2Fpyproject.toml" alt="Python Version">
47
+ <a href="https://github.com/sponsors/julienld"><img src="https://img.shields.io/badge/GitHub_Sponsors-☕-blueviolet" alt="GitHub Sponsors"></a>
48
+ </p>
49
+
50
+ <p align="center">
51
+ <em>A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with Home Assistant.<br>
52
+ Using natural language, control smart home devices, query states, execute services and manage your automations.</em>
53
+ </p>
54
+ </div>
55
+
56
+ ---
57
+
58
+ ![Demo with Claude Desktop](docs/img/demo.webp)
59
+
60
+ ---
61
+
62
+ ## 🚀 Get Started
63
+
64
+ ### Full guide to get you started with Claude Desktop (~10 min)
65
+
66
+ *No paid subscription required.* Click on your operating system:
67
+
68
+ <p>
69
+ <a href="https://homeassistant-ai.github.io/ha-mcp/guide-macos/"><img src="https://img.shields.io/badge/Setup_Guide_for_macOS-000000?style=for-the-badge&logo=apple&logoColor=white" alt="Setup Guide for macOS" height="120"></a>&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://homeassistant-ai.github.io/ha-mcp/guide-windows/"><img src="https://img.shields.io/badge/Setup_Guide_for_Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white" alt="Setup Guide for Windows" height="120"></a>
70
+ </p>
71
+
72
+ ### Quick install (~5 min)
73
+
74
+ <details>
75
+ <summary><b>🍎 macOS</b></summary>
76
+
77
+ 1. Go to [claude.ai](https://claude.ai) and sign in (or create a free account)
78
+ 2. Open **Terminal** and run:
79
+ ```sh
80
+ curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-macos.sh | sh
81
+ ```
82
+ 3. [Download Claude Desktop](https://claude.ai/download) (or restart: Claude menu → Quit)
83
+ 4. Ask Claude: **"Can you see my Home Assistant?"**
84
+
85
+ You're now connected to the demo environment! [Connect your own Home Assistant →](https://homeassistant-ai.github.io/ha-mcp/guide-macos/#step-6-connect-your-home-assistant)
86
+
87
+ </details>
88
+
89
+ <details>
90
+ <summary><b>🪟 Windows</b></summary>
91
+
92
+ 1. Go to [claude.ai](https://claude.ai) and sign in (or create a free account)
93
+ 2. Open **Windows PowerShell** (from Start menu) and run:
94
+ ```powershell
95
+ irm https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-windows.ps1 | iex
96
+ ```
97
+ 3. [Download Claude Desktop](https://claude.ai/download) (or restart: File → Exit)
98
+ 4. Ask Claude: **"Can you see my Home Assistant?"**
99
+
100
+ You're now connected to the demo environment! [Connect your own Home Assistant →](https://homeassistant-ai.github.io/ha-mcp/guide-windows/#step-6-connect-your-home-assistant)
101
+
102
+ </details>
103
+
104
+ ### 🧙 Setup Wizard for 15+ clients
105
+
106
+ **Claude Code, Gemini CLI, ChatGPT, Open WebUI, VSCode, Cursor, and more.**
107
+
108
+ <p>
109
+ <a href="https://homeassistant-ai.github.io/ha-mcp/setup/"><img src="https://img.shields.io/badge/Open_Setup_Wizard-4A90D9?style=for-the-badge" alt="Open Setup Wizard" height="40"></a>
110
+ </p>
111
+
112
+ Having issues? Check the **[FAQ & Troubleshooting](https://homeassistant-ai.github.io/ha-mcp/faq/)**
113
+
114
+ ---
115
+
116
+ ## 💬 What Can You Do With It?
117
+
118
+ Just talk to Claude naturally. Here are some real examples:
119
+
120
+ | You Say | What Happens |
121
+ |---------|--------------|
122
+ | *"Create an automation that turns on the porch light at sunset"* | Creates the automation with proper triggers and actions |
123
+ | *"Add a weather card to my dashboard"* | Updates your Lovelace dashboard with the new card |
124
+ | *"The motion sensor automation isn't working, debug it"* | Analyzes execution traces, identifies the issue, suggests fixes |
125
+ | *"Make my morning routine automation also turn on the coffee maker"* | Reads the existing automation, adds the new action, updates it |
126
+ | *"Create a script that sets movie mode: dim lights, close blinds, turn on TV"* | Creates a reusable script with the sequence of actions |
127
+
128
+ Spend less time configuring, more time enjoying your smart home.
129
+
130
+ ---
131
+
132
+ ## ✨ Features
133
+
134
+ | Category | Capabilities |
135
+ |----------|--------------|
136
+ | **🔍 Search** | Fuzzy entity search, deep config search, system overview |
137
+ | **🏠 Control** | Any service, bulk device control, real-time states |
138
+ | **🔧 Manage** | Automations, scripts, helpers, dashboards, areas, zones, groups, calendars, blueprints |
139
+ | **📊 Monitor** | History, statistics, camera snapshots, automation traces, ZHA devices |
140
+ | **💾 System** | Backup/restore, updates, add-ons, device registry |
141
+ | **🔐 Auth** | OAuth 2.1 with DCR for secure multi-user access (Claude.ai), token-based for local clients |
142
+
143
+ <details>
144
+ <summary><b>🛠️ Complete Tool List (97 tools)</b></summary>
145
+
146
+ | Category | Tools |
147
+ |----------|-------|
148
+ | **Search & Discovery** | `ha_search_entities`, `ha_deep_search`, `ha_get_overview`, `ha_get_state` |
149
+ | **Service & Device Control** | `ha_call_service`, `ha_bulk_control`, `ha_get_operation_status`, `ha_get_bulk_status`, `ha_list_services` |
150
+ | **Automations** | `ha_config_get_automation`, `ha_config_set_automation`, `ha_config_remove_automation` |
151
+ | **Scripts** | `ha_config_get_script`, `ha_config_set_script`, `ha_config_remove_script` |
152
+ | **Helper Entities** | `ha_config_list_helpers`, `ha_config_set_helper`, `ha_config_remove_helper` |
153
+ | **Dashboards** | `ha_config_get_dashboard`, `ha_config_set_dashboard`, `ha_config_update_dashboard_metadata`, `ha_config_delete_dashboard`, `ha_get_dashboard_guide`, `ha_get_card_types`, `ha_get_card_documentation` |
154
+ | **Areas & Floors** | `ha_config_list_areas`, `ha_config_set_area`, `ha_config_remove_area`, `ha_config_list_floors`, `ha_config_set_floor`, `ha_config_remove_floor` |
155
+ | **Labels** | `ha_config_get_label`, `ha_config_set_label`, `ha_config_remove_label`, `ha_manage_entity_labels` |
156
+ | **Zones** | `ha_get_zone`, `ha_create_zone`, `ha_update_zone`, `ha_delete_zone` |
157
+ | **Groups** | `ha_config_list_groups`, `ha_config_set_group`, `ha_config_remove_group` |
158
+ | **Todo Lists** | `ha_get_todo`, `ha_add_todo_item`, `ha_update_todo_item`, `ha_remove_todo_item` |
159
+ | **Calendar** | `ha_config_get_calendar_events`, `ha_config_set_calendar_event`, `ha_config_remove_calendar_event` |
160
+ | **Blueprints** | `ha_list_blueprints`, `ha_get_blueprint`, `ha_import_blueprint` |
161
+ | **Device Registry** | `ha_get_device`, `ha_update_device`, `ha_remove_device`, `ha_rename_entity` |
162
+ | **ZHA & Integrations** | `ha_get_zha_devices`, `ha_get_entity_integration_source` |
163
+ | **Add-ons** | `ha_get_addon` |
164
+ | **Camera** | `ha_get_camera_image` |
165
+ | **History & Statistics** | `ha_get_history`, `ha_get_statistics` |
166
+ | **Automation Traces** | `ha_get_automation_traces` |
167
+ | **System & Updates** | `ha_check_config`, `ha_restart`, `ha_reload_core`, `ha_get_system_info`, `ha_get_system_health`, `ha_get_updates` |
168
+ | **Backup & Restore** | `ha_backup_create`, `ha_backup_restore` |
169
+ | **Utility** | `ha_get_logbook`, `ha_eval_template`, `ha_get_domain_docs`, `ha_get_integration` |
170
+
171
+ </details>
172
+
173
+ ---
174
+
175
+ ## 🤝 Contributing
176
+
177
+ For development setup, testing instructions, and contribution guidelines, see **[CONTRIBUTING.md](CONTRIBUTING.md)**.
178
+
179
+ For comprehensive testing documentation, see **[tests/README.md](tests/README.md)**.
180
+
181
+ ---
182
+
183
+ ## 🔒 Privacy
184
+
185
+ Ha-mcp runs **locally** on your machine. Your smart home data stays on your network.
186
+
187
+ - **Configurable telemetry** — optional anonymous usage stats
188
+ - **No personal data collection** — we never collect entity names, configs, or device data
189
+ - **User-controlled bug reports** — only sent with your explicit approval
190
+
191
+ For full details, see our [Privacy Policy](PRIVACY.md).
192
+
193
+ ---
194
+
195
+ ## 📄 License
196
+
197
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
198
+
199
+ ---
200
+
201
+ ## 🙏 Acknowledgments
202
+
203
+ - **[Home Assistant](https://home-assistant.io/)**: Amazing smart home platform (!)
204
+ - **[FastMCP](https://github.com/jlowin/fastmcp)**: Excellent MCP server framework
205
+ - **[Model Context Protocol](https://modelcontextprotocol.io/)**: Standardized AI-application communication
206
+ - **[Claude Code](https://github.com/anthropics/claude-code)**: AI-powered coding assistant
207
+
208
+ ## 👥 Contributors
209
+
210
+ - **[@julienld](https://github.com/julienld)** — Project maintainer & core contributor.
211
+ - **[@kingbear2](https://github.com/kingbear2)** — Windows UV setup guide.
212
+ - **[@sergeykad](https://github.com/sergeykad)** — Dashboard card-level CRUD operations, better changelogs and removed the dependency to textdistance/numpy.
213
+ - **[@konradwalsh](https://github.com/konradwalsh)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
214
+ - **[@cj-elevate](https://github.com/cj-elevate)** — Integration & entity management tools (enable/disable/delete).
215
+ - **[@kingpanther13](https://github.com/kingpanther13)** — Native solutions guidance to reduce template overuse.
216
+ - **[@Raygooo](https://github.com/Raygooo)** — SOCKS proxy support.
217
+
218
+ ---
219
+
220
+ ## 💬 Community
221
+
222
+ - **[GitHub Discussions](https://github.com/homeassistant-ai/ha-mcp/discussions)** — Ask questions, share ideas
223
+ - **[Issue Tracker](https://github.com/homeassistant-ai/ha-mcp/issues)** — Report bugs, request features, or suggest tool behavior improvements
224
+
225
+ ---
226
+
227
+ ## ⭐ Star History
228
+
229
+ [![Star History Chart](https://api.star-history.com/svg?repos=homeassistant-ai/ha-mcp&type=Date)](https://star-history.com/#homeassistant-ai/ha-mcp&Date)
@@ -0,0 +1,71 @@
1
+ ha_mcp/__init__.py,sha256=WnpUJgSF99nlyZeI5nppDjYZ7RqtjAERLpGHiFWOcHQ,1308
2
+ ha_mcp/__main__.py,sha256=sUP5bRI2g1QjKojmz0UhTB5kpAROBMgYhUsg9MspI7k,24583
3
+ ha_mcp/_pypi_marker,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ ha_mcp/config.py,sha256=djlKf9ozL-xC1TuzzkWN4ECbXytkRxZhR3s2CYMuEjI,6427
5
+ ha_mcp/errors.py,sha256=6st0Kud3iACAHaKn-eEb8O5o8Ee9ZcSxpeeDJBRM844,13568
6
+ ha_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ ha_mcp/server.py,sha256=FkioX-XOAkOyE1AfsBuVnA51kkYH9-N12HLuHXBMxso,7163
8
+ ha_mcp/smoke_test.py,sha256=2aABoHVaxNRIFaLKHZdtLr_d1yCYtB6ZUVAU2AWUlIM,3804
9
+ ha_mcp/auth/__init__.py,sha256=4LY5fPgwJvbWFPc9AndpCkvg8PqssN2WCmlQWqLbJgI,304
10
+ ha_mcp/auth/consent_form.py,sha256=SBuhr7A87T2Npkk1BMlmnoSrzgETDDLgJ8i0godUiYs,14708
11
+ ha_mcp/auth/provider.py,sha256=5s55oo7iQjnpPuhgTt-meapLwPIC4r1v_5wrUQ15y8o,30949
12
+ ha_mcp/client/__init__.py,sha256=tPxyY6vQu5up4kwJgqc-ChpjPEBK5QouUedMQSNJdUQ,277
13
+ ha_mcp/client/rest_client.py,sha256=DC_BPnxLbl5UJ-eUo7bXh4oK1b-IqfpA695z6SS8m9E,34505
14
+ ha_mcp/client/websocket_client.py,sha256=Qygz6D79ZS7bJzk6u-DP1Gh7Z_ozfhOzDu-jz-Pglss,24112
15
+ ha_mcp/client/websocket_listener.py,sha256=5LpBqMlbTqaW3oX61t6OAfwbKy4DurKNeZPPzKIMPiU,12587
16
+ ha_mcp/resources/card_types.json,sha256=dRcKOqMpW6zIh2Q2MLNi8woK9gV_aY1YB5Ot9EzUu_M,977
17
+ ha_mcp/resources/dashboard_guide.md,sha256=uJPV5_dmmoS8YFJX98RA_gflUPFyvVkjU-rDJcqQOkw,14967
18
+ ha_mcp/tools/__init__.py,sha256=7aPElrhC2sUXIgCExm3hIlgSYYdi6KyvmfhtOzPFQHM,334
19
+ ha_mcp/tools/backup.py,sha256=BB1BGWPVneebM7Rf7uq0V9hEeKqmlibBrZSavdNy_Xk,17626
20
+ ha_mcp/tools/device_control.py,sha256=QQkx3CENpfNQIf1UA681Bq4GClMx2pJDZR6ZWbX_mGc,27484
21
+ ha_mcp/tools/enhanced.py,sha256=WJgpSgAJOEhuzPOERiJtasPpGE28wgoJ3vvurgP_Wrw,6681
22
+ ha_mcp/tools/helpers.py,sha256=0Z5jGxNVpm6TnwKO7ZO5zOmG-ankhAXprt_xgcfLE1g,5837
23
+ ha_mcp/tools/registry.py,sha256=mTrju-6gbmhlX-nOYOMpwqiN7zdOCQetnBihHBVq50E,7721
24
+ ha_mcp/tools/smart_search.py,sha256=iMtHjCcL9GfDgD0S4KN3I33b1MXHQXh00W05TmTQU2A,34454
25
+ ha_mcp/tools/tools_addons.py,sha256=xEvYfE_tGovvaczIA5O8uugrnsNr6gDFSSW1jNmNfXg,12369
26
+ ha_mcp/tools/tools_areas.py,sha256=aVdAAwqTPk34ZCP6rhxIo_cw01Cj4Dwa2F06gmvIk9A,17792
27
+ ha_mcp/tools/tools_blueprints.py,sha256=ZH4JHheknfhLzSR104Uk4Ulx7wVHFx8IAxJPQdTlIt0,10911
28
+ ha_mcp/tools/tools_bug_report.py,sha256=2sBSPemnSk6hwSvEZnrLkTLIrlGVn7uTIRT-YZhK8f0,18784
29
+ ha_mcp/tools/tools_calendar.py,sha256=Okt6FdBe7oQP6uXggfR8yET-YjczY9-fn9xuIEnPXq8,14767
30
+ ha_mcp/tools/tools_camera.py,sha256=tDrFcoX9tDfLdwCWTD5l7mErYVNBoAy7j_LsMctpspI,5769
31
+ ha_mcp/tools/tools_config_automations.py,sha256=DCr28CK1bcJDu28ButGZAaapzsmA7oGL5N-bzUXeTQU,20786
32
+ ha_mcp/tools/tools_config_dashboards.py,sha256=qRReRlje64l6GGeO6mYCJ7LD8qRdemLavJuzLWyfMMA,60607
33
+ ha_mcp/tools/tools_config_entry_flow.py,sha256=G21DCsD1eDRN9uq3bnDicc8opcpd5gRSxITpKklsR60,7839
34
+ ha_mcp/tools/tools_config_helpers.py,sha256=Qd6iEQx1EmihbO3abMuyfw0Jo-jCNmLuhqNVFmWdTlQ,37694
35
+ ha_mcp/tools/tools_config_info.py,sha256=79TBsFijVQQuiAG6FXeJyMEGkKQxehtOCdKpxUzEtJU,10762
36
+ ha_mcp/tools/tools_config_scripts.py,sha256=vp5laEBb0m73DSlH4vwT7sOFlEqYs_xX_Uy7hC3bQe0,10017
37
+ ha_mcp/tools/tools_entities.py,sha256=uFLx7xgtBnD72MDyKUp24l_5HCvu-ZQ84lWkUglid90,2623
38
+ ha_mcp/tools/tools_filesystem.py,sha256=DXVwIi6x_SnfrR0ZvdlwWIWw-uOttd_wAm5idiMdi-8,20102
39
+ ha_mcp/tools/tools_groups.py,sha256=KpDpUpIlqn7-ZfvdhQKWPrQ3-q7bSq5_MABZjVejjqU,11786
40
+ ha_mcp/tools/tools_hacs.py,sha256=gWTO-SRi7CPx1MCj68Kw94EJO9r1EVduEdwMdnoXfDw,34988
41
+ ha_mcp/tools/tools_history.py,sha256=CgUCi2_T3F536mpx03OgnHD0pCtXtpDe5CnU_VGNLvA,28704
42
+ ha_mcp/tools/tools_integrations.py,sha256=6PH_gQAkvPkGvW4tdd0TU_6qaZVBfnKWjPEVFUeo7uY,11645
43
+ ha_mcp/tools/tools_labels.py,sha256=nJm2bX5i9OGYMLIpSjeBaEWIhOolbgc1Y5L66OIYL08,27150
44
+ ha_mcp/tools/tools_mcp_component.py,sha256=id6PptzVb06AAb6-ydxGDQPHH5gFGn9RzchZvUOeug0,12871
45
+ ha_mcp/tools/tools_registry.py,sha256=xIhIYeKr-sOC2bP8uglgL-576BoDnweBHmzhwk-KPzQ,46054
46
+ ha_mcp/tools/tools_resources.py,sha256=4s6aflsV7eP5vkz_LHC960OhzUizfQPyocTPBNyc_-k,22945
47
+ ha_mcp/tools/tools_search.py,sha256=4p3I5f6DPeix_KWgwQxC3I-nEokEze3JcntohzoCkos,26319
48
+ ha_mcp/tools/tools_service.py,sha256=eylo6w0mxjv3JcufCZPZre5U_D4izmc8q20ML66nwU0,9062
49
+ ha_mcp/tools/tools_services.py,sha256=KgWt75Rnnk2XQHOgZecf0vOFR0vNO1LFKWh6Mj39aTw,10895
50
+ ha_mcp/tools/tools_system.py,sha256=uHu8CVsALjgpVG21h3vEnntmB3sNP0yGD6d01e9xFdM,14210
51
+ ha_mcp/tools/tools_todo.py,sha256=l71w_ln_Xjy7PbQ5k4Qcf15OJvNFEeihWxyQxfHkmLg,19851
52
+ ha_mcp/tools/tools_traces.py,sha256=F_ppsGNqOEmnav3xgPtQSer8NykzUnArEs2R3qO6K2A,18548
53
+ ha_mcp/tools/tools_updates.py,sha256=Va8OD1h53TODbrtqKkLMAeLm8ZQaNwwAfPSwHsiPgCo,18357
54
+ ha_mcp/tools/tools_utility.py,sha256=OIuxFx-SdTsenRyChlTDNTtOS8iJLkj59IrGq2UyefQ,20886
55
+ ha_mcp/tools/tools_voice_assistant.py,sha256=ylcoFQzYFGSeRN3ULKUwk938Q7ykXtOPILhcZGwzKzQ,13158
56
+ ha_mcp/tools/tools_zones.py,sha256=M1hzXSrC59-kb3aFTNR9x8LbjOv6xVpdOyhdXs5ckt8,14083
57
+ ha_mcp/tools/util_helpers.py,sha256=yuey0tLVo5MZm_-kkDzp1rxYz27PMx6P7lttdlSpTw0,6164
58
+ ha_mcp/utils/__init__.py,sha256=mSXOwk11PxZeFLE7kygZsL53JSfZ53pFZ7MhjbJNaGQ,736
59
+ ha_mcp/utils/domain_handlers.py,sha256=k99mzMDnhCkptIdub8pbZq7RN3hrAjzySBWfd6uSC9I,12334
60
+ ha_mcp/utils/fuzzy_search.py,sha256=QD3Kd-MulX4japp9tj888eMoifTZvILV_iSogWAd8Pc,11377
61
+ ha_mcp/utils/operation_manager.py,sha256=cFW_PFuvU221IqQ2wZDc7o9gf2mffAX_EtePh7PTbhA,14332
62
+ ha_mcp/utils/usage_logger.py,sha256=dHkHzBCCrLMM1n0aZEWjVgOYjMtQvS3FGMRMbIcgUvw,9303
63
+ ha_mcp_dev-6.3.1.dev137.dist-info/licenses/LICENSE,sha256=7rJXXKBJWgJF8595wk-YTxwVTEi1kQaIqyy9dh5o_oY,1062
64
+ tests/__init__.py,sha256=YRpec-ZFYCJ48oD_7ZcNY7dB8avoTWOrZICjaM-BYJ0,39
65
+ tests/test_constants.py,sha256=F14Pf5QMzG77RhsecaNWWaEL-B_8ykHJLIvVMcJxT8M,609
66
+ tests/test_env_manager.py,sha256=patnM4If8gLUGuFRQEYMAuNhsAvjLKZZYuR2oJ2c2kU,12011
67
+ ha_mcp_dev-6.3.1.dev137.dist-info/METADATA,sha256=M2ztuZQ9Gkl2R3a2d6VnOVnTwO_p5O3nA0HndzBksLg,11677
68
+ ha_mcp_dev-6.3.1.dev137.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
69
+ ha_mcp_dev-6.3.1.dev137.dist-info/entry_points.txt,sha256=MN0GhQEca3hlfJIBAclzzbvwU7K-czkhlc55Gn58vdg,254
70
+ ha_mcp_dev-6.3.1.dev137.dist-info/top_level.txt,sha256=cqJLEmgh4gQBKg_vBqj0ahS4DCg4J0qBXYgZCDQ2IWs,13
71
+ ha_mcp_dev-6.3.1.dev137.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,7 @@
1
+ [console_scripts]
2
+ ha-mcp = ha_mcp.__main__:main
3
+ ha-mcp-oauth = ha_mcp.__main__:main_oauth
4
+ ha-mcp-smoke-test = ha_mcp.smoke_test:main
5
+ ha-mcp-sse = ha_mcp.__main__:main_sse
6
+ ha-mcp-web = ha_mcp.__main__:main_web
7
+ hamcp-test-env = tests.test_env_manager:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Julien
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ ha_mcp
2
+ tests
tests/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Home Assistant MCP Server Tests."""
@@ -0,0 +1,15 @@
1
+ """
2
+ Test constants shared across test modules.
3
+
4
+ This module centralizes test configuration values to ensure consistency
5
+ across all test environments.
6
+ """
7
+
8
+ # Long-lived access token for test Home Assistant instance
9
+ # This token is embedded in tests/initial_test_state/.storage/auth
10
+ # Expires: 2035 (10+ years from token creation)
11
+ TEST_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIxOTE5ZTZlMTVkYjI0Mzk2YTQ4YjFiZTI1MDM1YmU2YSIsImlhdCI6MTc1NzI4OTc5NiwiZXhwIjoyMDcyNjQ5Nzk2fQ.Yp9SSAjm2gvl9Xcu96FFxS8SapHxWAVzaI0E3cD9xac"
12
+
13
+ # Test user credentials (for UI access)
14
+ TEST_USER = "mcp"
15
+ TEST_PASSWORD = "mcp"