@coffeerunhobby/mcp-ha-connect 0.5.0

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 (294) hide show
  1. package/.dockerignore +8 -0
  2. package/.env.example +149 -0
  3. package/.gitattributes +30 -0
  4. package/Dockerfile +33 -0
  5. package/LICENSE +21 -0
  6. package/README.md +394 -0
  7. package/dist/config.d.ts +33 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +183 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/haClient/automations.d.ts +67 -0
  12. package/dist/haClient/automations.d.ts.map +1 -0
  13. package/dist/haClient/automations.js +181 -0
  14. package/dist/haClient/automations.js.map +1 -0
  15. package/dist/haClient/config.d.ts +28 -0
  16. package/dist/haClient/config.d.ts.map +1 -0
  17. package/dist/haClient/config.js +38 -0
  18. package/dist/haClient/config.js.map +1 -0
  19. package/dist/haClient/devices.d.ts +219 -0
  20. package/dist/haClient/devices.d.ts.map +1 -0
  21. package/dist/haClient/devices.js +506 -0
  22. package/dist/haClient/devices.js.map +1 -0
  23. package/dist/haClient/entities.d.ts +35 -0
  24. package/dist/haClient/entities.d.ts.map +1 -0
  25. package/dist/haClient/entities.js +97 -0
  26. package/dist/haClient/entities.js.map +1 -0
  27. package/dist/haClient/events.d.ts +138 -0
  28. package/dist/haClient/events.d.ts.map +1 -0
  29. package/dist/haClient/events.js +344 -0
  30. package/dist/haClient/events.js.map +1 -0
  31. package/dist/haClient/history.d.ts +26 -0
  32. package/dist/haClient/history.d.ts.map +1 -0
  33. package/dist/haClient/history.js +54 -0
  34. package/dist/haClient/history.js.map +1 -0
  35. package/dist/haClient/index.d.ts +153 -0
  36. package/dist/haClient/index.d.ts.map +1 -0
  37. package/dist/haClient/index.js +223 -0
  38. package/dist/haClient/index.js.map +1 -0
  39. package/dist/haClient/request.d.ts +34 -0
  40. package/dist/haClient/request.d.ts.map +1 -0
  41. package/dist/haClient/request.js +93 -0
  42. package/dist/haClient/request.js.map +1 -0
  43. package/dist/haClient/services.d.ts +22 -0
  44. package/dist/haClient/services.d.ts.map +1 -0
  45. package/dist/haClient/services.js +39 -0
  46. package/dist/haClient/services.js.map +1 -0
  47. package/dist/haClient/states.d.ts +26 -0
  48. package/dist/haClient/states.d.ts.map +1 -0
  49. package/dist/haClient/states.js +53 -0
  50. package/dist/haClient/states.js.map +1 -0
  51. package/dist/haClient/updates.d.ts +22 -0
  52. package/dist/haClient/updates.d.ts.map +1 -0
  53. package/dist/haClient/updates.js +48 -0
  54. package/dist/haClient/updates.js.map +1 -0
  55. package/dist/haClient.d.ts +97 -0
  56. package/dist/haClient.d.ts.map +1 -0
  57. package/dist/haClient.js +344 -0
  58. package/dist/haClient.js.map +1 -0
  59. package/dist/index.d.ts +8 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +92 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/localAI/index.d.ts +37 -0
  64. package/dist/localAI/index.d.ts.map +1 -0
  65. package/dist/localAI/index.js +73 -0
  66. package/dist/localAI/index.js.map +1 -0
  67. package/dist/localAI/prompts.d.ts +20 -0
  68. package/dist/localAI/prompts.d.ts.map +1 -0
  69. package/dist/localAI/prompts.js +48 -0
  70. package/dist/localAI/prompts.js.map +1 -0
  71. package/dist/localAI/providers/base.d.ts +51 -0
  72. package/dist/localAI/providers/base.d.ts.map +1 -0
  73. package/dist/localAI/providers/base.js +80 -0
  74. package/dist/localAI/providers/base.js.map +1 -0
  75. package/dist/localAI/providers/ollama.d.ts +22 -0
  76. package/dist/localAI/providers/ollama.d.ts.map +1 -0
  77. package/dist/localAI/providers/ollama.js +63 -0
  78. package/dist/localAI/providers/ollama.js.map +1 -0
  79. package/dist/localAI/providers/openai.d.ts +23 -0
  80. package/dist/localAI/providers/openai.d.ts.map +1 -0
  81. package/dist/localAI/providers/openai.js +63 -0
  82. package/dist/localAI/providers/openai.js.map +1 -0
  83. package/dist/localAI/types.d.ts +61 -0
  84. package/dist/localAI/types.d.ts.map +1 -0
  85. package/dist/localAI/types.js +6 -0
  86. package/dist/localAI/types.js.map +1 -0
  87. package/dist/ollamaClient.d.ts +37 -0
  88. package/dist/ollamaClient.d.ts.map +1 -0
  89. package/dist/ollamaClient.js +110 -0
  90. package/dist/ollamaClient.js.map +1 -0
  91. package/dist/resources/index.d.ts +11 -0
  92. package/dist/resources/index.d.ts.map +1 -0
  93. package/dist/resources/index.js +238 -0
  94. package/dist/resources/index.js.map +1 -0
  95. package/dist/server/common.d.ts +9 -0
  96. package/dist/server/common.d.ts.map +1 -0
  97. package/dist/server/common.js +27 -0
  98. package/dist/server/common.js.map +1 -0
  99. package/dist/server/eventSubscription.d.ts +53 -0
  100. package/dist/server/eventSubscription.d.ts.map +1 -0
  101. package/dist/server/eventSubscription.js +257 -0
  102. package/dist/server/eventSubscription.js.map +1 -0
  103. package/dist/server/http.d.ts +12 -0
  104. package/dist/server/http.d.ts.map +1 -0
  105. package/dist/server/http.js +491 -0
  106. package/dist/server/http.js.map +1 -0
  107. package/dist/server/rateLimiter.d.ts +70 -0
  108. package/dist/server/rateLimiter.d.ts.map +1 -0
  109. package/dist/server/rateLimiter.js +156 -0
  110. package/dist/server/rateLimiter.js.map +1 -0
  111. package/dist/server/sse.d.ts +32 -0
  112. package/dist/server/sse.d.ts.map +1 -0
  113. package/dist/server/sse.js +76 -0
  114. package/dist/server/sse.js.map +1 -0
  115. package/dist/server/stdio.d.ts +8 -0
  116. package/dist/server/stdio.d.ts.map +1 -0
  117. package/dist/server/stdio.js +16 -0
  118. package/dist/server/stdio.js.map +1 -0
  119. package/dist/server/stream.d.ts +26 -0
  120. package/dist/server/stream.d.ts.map +1 -0
  121. package/dist/server/stream.js +81 -0
  122. package/dist/server/stream.js.map +1 -0
  123. package/dist/server.d.ts +8 -0
  124. package/dist/server.d.ts.map +1 -0
  125. package/dist/server.js +30 -0
  126. package/dist/server.js.map +1 -0
  127. package/dist/tools/callService.d.ts +8 -0
  128. package/dist/tools/callService.d.ts.map +1 -0
  129. package/dist/tools/callService.js +100 -0
  130. package/dist/tools/callService.js.map +1 -0
  131. package/dist/tools/getClient.d.ts +8 -0
  132. package/dist/tools/getClient.d.ts.map +1 -0
  133. package/dist/tools/getClient.js +94 -0
  134. package/dist/tools/getClient.js.map +1 -0
  135. package/dist/tools/getDevice.d.ts +8 -0
  136. package/dist/tools/getDevice.d.ts.map +1 -0
  137. package/dist/tools/getDevice.js +94 -0
  138. package/dist/tools/getDevice.js.map +1 -0
  139. package/dist/tools/getEntitiesByDomain.d.ts +8 -0
  140. package/dist/tools/getEntitiesByDomain.d.ts.map +1 -0
  141. package/dist/tools/getEntitiesByDomain.js +80 -0
  142. package/dist/tools/getEntitiesByDomain.js.map +1 -0
  143. package/dist/tools/getState.d.ts +8 -0
  144. package/dist/tools/getState.d.ts.map +1 -0
  145. package/dist/tools/getState.js +90 -0
  146. package/dist/tools/getState.js.map +1 -0
  147. package/dist/tools/getStates.d.ts +8 -0
  148. package/dist/tools/getStates.d.ts.map +1 -0
  149. package/dist/tools/getStates.js +70 -0
  150. package/dist/tools/getStates.js.map +1 -0
  151. package/dist/tools/index.d.ts +9 -0
  152. package/dist/tools/index.d.ts.map +1 -0
  153. package/dist/tools/index.js +9 -0
  154. package/dist/tools/index.js.map +1 -0
  155. package/dist/tools/listClients.d.ts +8 -0
  156. package/dist/tools/listClients.d.ts.map +1 -0
  157. package/dist/tools/listClients.js +73 -0
  158. package/dist/tools/listClients.js.map +1 -0
  159. package/dist/tools/listDevices.d.ts +8 -0
  160. package/dist/tools/listDevices.d.ts.map +1 -0
  161. package/dist/tools/listDevices.js +73 -0
  162. package/dist/tools/listDevices.js.map +1 -0
  163. package/dist/tools/listSites.d.ts +8 -0
  164. package/dist/tools/listSites.d.ts.map +1 -0
  165. package/dist/tools/listSites.js +67 -0
  166. package/dist/tools/listSites.js.map +1 -0
  167. package/dist/tools/registry.d.ts +9 -0
  168. package/dist/tools/registry.d.ts.map +1 -0
  169. package/dist/tools/registry.js +1382 -0
  170. package/dist/tools/registry.js.map +1 -0
  171. package/dist/tools/searchDevices.d.ts +8 -0
  172. package/dist/tools/searchDevices.d.ts.map +1 -0
  173. package/dist/tools/searchDevices.js +84 -0
  174. package/dist/tools/searchDevices.js.map +1 -0
  175. package/dist/tools/searchEntities.d.ts +8 -0
  176. package/dist/tools/searchEntities.d.ts.map +1 -0
  177. package/dist/tools/searchEntities.js +80 -0
  178. package/dist/tools/searchEntities.js.map +1 -0
  179. package/dist/types/entity.d.ts +98 -0
  180. package/dist/types/entity.d.ts.map +1 -0
  181. package/dist/types/entity.js +2 -0
  182. package/dist/types/entity.js.map +1 -0
  183. package/dist/types/errors.d.ts +16 -0
  184. package/dist/types/errors.d.ts.map +1 -0
  185. package/dist/types/errors.js +32 -0
  186. package/dist/types/errors.js.map +1 -0
  187. package/dist/types/haConfig.d.ts +8 -0
  188. package/dist/types/haConfig.d.ts.map +1 -0
  189. package/dist/types/haConfig.js +2 -0
  190. package/dist/types/haConfig.js.map +1 -0
  191. package/dist/types/index.d.ts +6 -0
  192. package/dist/types/index.d.ts.map +1 -0
  193. package/dist/types/index.js +2 -0
  194. package/dist/types/index.js.map +1 -0
  195. package/dist/types/request.d.ts +7 -0
  196. package/dist/types/request.d.ts.map +1 -0
  197. package/dist/types/request.js +2 -0
  198. package/dist/types/request.js.map +1 -0
  199. package/dist/types/serviceCall.d.ts +18 -0
  200. package/dist/types/serviceCall.d.ts.map +1 -0
  201. package/dist/types/serviceCall.js +2 -0
  202. package/dist/types/serviceCall.js.map +1 -0
  203. package/dist/types.d.ts +2 -0
  204. package/dist/types.d.ts.map +1 -0
  205. package/dist/types.js +3 -0
  206. package/dist/types.js.map +1 -0
  207. package/dist/utils/config-validations.d.ts +12 -0
  208. package/dist/utils/config-validations.d.ts.map +1 -0
  209. package/dist/utils/config-validations.js +43 -0
  210. package/dist/utils/config-validations.js.map +1 -0
  211. package/dist/utils/logger.d.ts +24 -0
  212. package/dist/utils/logger.d.ts.map +1 -0
  213. package/dist/utils/logger.js +94 -0
  214. package/dist/utils/logger.js.map +1 -0
  215. package/dist/wrapper.d.ts +8 -0
  216. package/dist/wrapper.d.ts.map +1 -0
  217. package/dist/wrapper.js +301 -0
  218. package/dist/wrapper.js.map +1 -0
  219. package/docker-compose.yml +17 -0
  220. package/docs/CHANGELOG.md +86 -0
  221. package/docs/LOCAL_CLIENT.md +268 -0
  222. package/docs/SSE_API.md +341 -0
  223. package/eslint.config.js +33 -0
  224. package/n8n/ai-agent-safety-monitor.json +355 -0
  225. package/openapi.json +166 -0
  226. package/package.json +51 -0
  227. package/src/config.ts +260 -0
  228. package/src/haClient/automations.ts +217 -0
  229. package/src/haClient/config.ts +41 -0
  230. package/src/haClient/devices.ts +653 -0
  231. package/src/haClient/entities.ts +128 -0
  232. package/src/haClient/events.ts +456 -0
  233. package/src/haClient/history.ts +68 -0
  234. package/src/haClient/index.ts +287 -0
  235. package/src/haClient/request.ts +127 -0
  236. package/src/haClient/services.ts +45 -0
  237. package/src/haClient/states.ts +60 -0
  238. package/src/haClient/updates.ts +58 -0
  239. package/src/index.ts +99 -0
  240. package/src/localAI/index.ts +88 -0
  241. package/src/localAI/prompts.ts +50 -0
  242. package/src/localAI/providers/base.ts +120 -0
  243. package/src/localAI/providers/ollama.ts +78 -0
  244. package/src/localAI/providers/openai.ts +86 -0
  245. package/src/localAI/types.ts +67 -0
  246. package/src/resources/index.ts +266 -0
  247. package/src/server/common.ts +38 -0
  248. package/src/server/eventSubscription.ts +318 -0
  249. package/src/server/http.ts +546 -0
  250. package/src/server/rateLimiter.ts +195 -0
  251. package/src/server/sse.ts +120 -0
  252. package/src/server/stdio.ts +22 -0
  253. package/src/server/stream.ts +113 -0
  254. package/src/tools/callService.ts +114 -0
  255. package/src/tools/getEntitiesByDomain.ts +92 -0
  256. package/src/tools/getState.ts +103 -0
  257. package/src/tools/getStates.ts +80 -0
  258. package/src/tools/index.ts +9 -0
  259. package/src/tools/registry.ts +1521 -0
  260. package/src/tools/searchEntities.ts +92 -0
  261. package/src/types/entity.ts +110 -0
  262. package/src/types/errors.ts +33 -0
  263. package/src/types/haConfig.ts +7 -0
  264. package/src/types/index.ts +5 -0
  265. package/src/types/request.ts +6 -0
  266. package/src/types/serviceCall.ts +18 -0
  267. package/src/types.ts +2 -0
  268. package/src/utils/config-validations.ts +46 -0
  269. package/src/utils/logger.ts +114 -0
  270. package/src/wrapper.ts +340 -0
  271. package/tests/config.test.ts +332 -0
  272. package/tests/haClient/automations.test.ts +133 -0
  273. package/tests/haClient/config.test.ts +96 -0
  274. package/tests/haClient/entities.test.ts +188 -0
  275. package/tests/haClient/history.test.ts +170 -0
  276. package/tests/haClient/index.test.ts +221 -0
  277. package/tests/haClient/request.test.ts +217 -0
  278. package/tests/haClient/services.test.ts +120 -0
  279. package/tests/haClient/states.test.ts +119 -0
  280. package/tests/haClient/updates.test.ts +193 -0
  281. package/tests/index.test.ts +164 -0
  282. package/tests/localAI/index.test.ts +168 -0
  283. package/tests/localAI/ollama.test.ts +187 -0
  284. package/tests/localAI/openai.test.ts +257 -0
  285. package/tests/server/common.test.ts +64 -0
  286. package/tests/server/http.test.ts +326 -0
  287. package/tests/server/sse.test.ts +240 -0
  288. package/tests/server/stdio.test.ts +102 -0
  289. package/tests/server/stream.test.ts +287 -0
  290. package/tests/tools/registry.test.ts +355 -0
  291. package/tests/utils/config-validations.test.ts +289 -0
  292. package/tests/utils/logger.test.ts +327 -0
  293. package/tsconfig.json +25 -0
  294. package/vitest.config.ts +26 -0
@@ -0,0 +1,268 @@
1
+ # Local Client
2
+
3
+ ## Mode: Standalone Node JS version
4
+
5
+ Add to your Claude Desktop configuration file:
6
+
7
+ **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
8
+ **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
9
+
10
+ ```json
11
+ {
12
+ "mcpServers": {
13
+ "home-assistant": {
14
+ "command": "node",
15
+ "args": [
16
+ "--env-file=.env",
17
+ "C:\\path\\to\\mcp-ha-connect\\dist\\index.js"
18
+ ]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ Or specify environment variables directly:
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "home-assistant": {
30
+ "command": "node",
31
+ "args": [
32
+ "C:\\path\\to\\mcp-ha-connect\\dist\\index.js"
33
+ ],
34
+ "env": {
35
+ "HA_URL": "http://homeassistant.10.0.0.19.nip.io:8123",
36
+ "HA_TOKEN": "your_long_lived_access_token",
37
+ "HA_STRICT_SSL": "false",
38
+ "MCP_SERVER_LOG_LEVEL": "info",
39
+ "AI_URL": "http://ollama.10.0.0.1.nip.io:11434",
40
+ "AI_MODEL": "phi4:14b"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Mode: Docker container with .env
48
+
49
+ You need docker runtime installed locally
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "mcp-ha-connect": {
55
+ "command": "docker",
56
+ "args": [
57
+ "run",
58
+ "--rm",
59
+ "-i",
60
+ "--env-file",
61
+ "C:\\path\\to\\mcp-ha-connect\\.env",
62
+ "ghcr.io/coffeerunhobby/mcp-ha-connect:latest"
63
+ ]
64
+ }
65
+ }
66
+ }
67
+ ```
68
+
69
+ ### Mode: UVX proxy to a working mcp-ha-connect server
70
+
71
+ You can use mcp-remote via uvx to proxy to your remote MCP server.
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "home-assistant": {
77
+ "command": "uvx",
78
+ "args": [
79
+ "mcp-remote",
80
+ "http://mcp-ha.10.0.0.1.nip.io:3000/mcp"
81
+ ]
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ **nip.io Examples:**
88
+
89
+ [nip.io](https://nip.io) provides wildcard DNS for any IP address. Supported formats:
90
+
91
+ | Format | Example | Resolves To |
92
+ |--------|---------|-------------|
93
+ | Dot notation | `mcp.10.0.0.1.nip.io` | 10.0.0.1 |
94
+ | Dash notation | `mcp-10-0-0-1.nip.io` | 10.0.0.1 |
95
+ | Hex notation | `mcp-0a000001.nip.io` | 10.0.0.1 |
96
+ | With subdomain | `ha.mcp.10.0.0.1.nip.io` | 10.0.0.1 |
97
+
98
+ Replace `10.0.0.1` with your actual server IP address.
99
+
100
+ ### Mode: Claude Desktop with Docker (Stdio Transport)
101
+
102
+ If you prefer to run the MCP server in Docker and connect Claude Desktop to it via stdio, you can use `docker exec`.
103
+
104
+ **Prerequisites:**
105
+ - Docker container running with MCP server
106
+ - Container must be running in stdio mode (not HTTP)
107
+
108
+ **Step 1: Start Docker Container**
109
+
110
+ ```bash
111
+ # Build image
112
+ docker build -t mcp-ha-connect .
113
+
114
+ # Run container in stdio mode (detached, but ready for exec)
115
+ docker run -d --name mcp-ha-connect \
116
+ -e HA_URL=http://homeassistant.10.0.0.19.nip.io:8123 \
117
+ -e HA_TOKEN=your_token \
118
+ -e AI_URL=http://ollama.10.0.0.17.nip.io:11434 \
119
+ -e AI_MODEL=phi4:14b \
120
+ -e MCP_SERVER_USE_HTTP=false \
121
+ mcp-ha-connect \
122
+ tail -f /dev/null
123
+ ```
124
+
125
+ **Note:** The `tail -f /dev/null` keeps the container running. The MCP server will be executed via `docker exec` by Claude Desktop.
126
+
127
+ **Step 2: Configure Claude Desktop**
128
+
129
+ Add to your Claude Desktop configuration file:
130
+
131
+ **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
132
+ **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
133
+
134
+ ```json
135
+ {
136
+ "mcpServers": {
137
+ "home-assistant": {
138
+ "command": "docker",
139
+ "args": [
140
+ "exec",
141
+ "-i",
142
+ "mcp-ha-connect",
143
+ "node",
144
+ "dist/index.js"
145
+ ],
146
+ "env": {
147
+ "HA_URL": "http://homeassistant.10.0.0.19.nip.io:8123",
148
+ "HA_TOKEN": "your_long_lived_access_token",
149
+ "AI_URL": "http://ollama.10.0.0.17.nip.io:11434",
150
+ "AI_MODEL": "phi4:14b",
151
+ "MCP_SERVER_LOG_LEVEL": "info"
152
+ }
153
+ }
154
+ }
155
+ }
156
+ ```
157
+
158
+ **Important Notes:**
159
+ - The `-i` flag (interactive) is required for stdio communication
160
+ - Environment variables are passed through the `env` object
161
+ - The Docker container must already be running
162
+ - The container name is `mcp-ha-connect` (change if different)
163
+
164
+ **Step 3: Restart Claude Desktop**
165
+
166
+ Close and reopen Claude Desktop for the configuration to take effect.
167
+
168
+ **Verification:**
169
+
170
+ ```bash
171
+ # Check if container is running
172
+ docker ps | grep mcp-ha-connect
173
+
174
+ # Test stdio communication
175
+ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | docker exec -i mcp-ha-connect node dist/index.js
176
+ ```
177
+
178
+ **Troubleshooting:**
179
+
180
+ If Claude Desktop can't connect:
181
+
182
+ ```bash
183
+ # Check container logs
184
+ docker logs mcp-ha-connect
185
+
186
+ # Test docker exec manually
187
+ docker exec -i mcp-ha-connect node dist/index.js
188
+
189
+ # Ensure container has access to Home Assistant
190
+ docker exec mcp-ha-connect curl http://homeassistant.10.0.0.19.nip.io:8123/api/
191
+
192
+ # Verify environment variables are set
193
+ docker exec mcp-ha-connect env | grep HA_
194
+ ```
195
+
196
+ ### Mode: Claude Desktop with Docker Compose
197
+
198
+ For a more robust Docker setup, use Docker Compose:
199
+
200
+ **Step 1: Create docker-compose.yml**
201
+
202
+ ```yaml
203
+ version: '3.8'
204
+
205
+ services:
206
+ mcp-ha-connect:
207
+ build: .
208
+ container_name: mcp-ha-connect
209
+ restart: unless-stopped
210
+ environment:
211
+ - HA_URL=http://homeassistant.10.0.0.19.nip.io:8123
212
+ - HA_TOKEN=${HA_TOKEN} # Load from .env file
213
+ - AI_URL=http://ollama.10.0.0.17.nip.io:11434
214
+ - AI_MODEL=phi4:14b
215
+ - MCP_SERVER_USE_HTTP=false
216
+ - MCP_SERVER_LOG_LEVEL=info
217
+ command: tail -f /dev/null
218
+ networks:
219
+ - home-automation
220
+
221
+ networks:
222
+ home-automation:
223
+ driver: bridge
224
+ ```
225
+
226
+ **Step 2: Create .env file**
227
+
228
+ ```bash
229
+ HA_TOKEN=your_long_lived_access_token_here
230
+ ```
231
+
232
+ **Step 3: Start with Docker Compose**
233
+
234
+ ```bash
235
+ # Start container
236
+ docker-compose up -d
237
+
238
+ # Check status
239
+ docker-compose ps
240
+ ```
241
+
242
+ **Step 4: Configure Claude Desktop**
243
+
244
+ ```json
245
+ {
246
+ "mcpServers": {
247
+ "home-assistant": {
248
+ "command": "docker",
249
+ "args": [
250
+ "exec",
251
+ "-i",
252
+ "mcp-ha-connect",
253
+ "node",
254
+ "dist/index.js"
255
+ ]
256
+ }
257
+ }
258
+ }
259
+ ```
260
+
261
+ **Note:** Environment variables are already set in the container via Docker Compose, so you don't need to specify them again in Claude Desktop config.
262
+
263
+ **Advantages of Docker Compose:**
264
+ - Easy multi-container management
265
+ - Automatic restart on failure
266
+ - Environment variable management via .env
267
+ - Network isolation
268
+ - Easy updates: `docker-compose pull && docker-compose up -d`
@@ -0,0 +1,341 @@
1
+ # SSE Event Subscription API
2
+
3
+ The MCP-HA-Connect server includes a powerful Server-Sent Events (SSE) system that provides real-time updates from your Home Assistant instance.
4
+
5
+ ## Overview
6
+
7
+ The SSE endpoint allows HTTP clients to subscribe to real-time events from Home Assistant, including:
8
+
9
+ - **State Changes**: Get instant notifications when any device state changes
10
+ - **Automation Triggers**: Monitor when automations are triggered
11
+ - **Service Calls**: Track service call executions
12
+ - **Domain-Specific Events**: Filter events by specific domains (lights, sensors, etc.)
13
+
14
+ ## Configuration
15
+
16
+ ### Environment Variables
17
+
18
+ ```bash
19
+ # Enable SSE event subscription (default: true)
20
+ MCP_SSE_EVENTS_ENABLED=true
21
+
22
+ # SSE endpoint path (default: /subscribe_events)
23
+ MCP_SSE_EVENTS_PATH=/subscribe_events
24
+ ```
25
+
26
+ ## Endpoint
27
+
28
+ ```
29
+ GET /subscribe_events
30
+ ```
31
+
32
+ ### Query Parameters
33
+
34
+ | Parameter | Type | Description |
35
+ |-----------|------|-------------|
36
+ | `domain` | string | Filter events by domain (e.g., `light`, `sensor`, `switch`) |
37
+ | `entity_id` | string | Filter events for a specific entity |
38
+ | `event_types` | string | Comma-separated list of event types to subscribe to |
39
+ | `token` | string | Optional authentication token (for future use) |
40
+
41
+ ### Examples
42
+
43
+ #### Subscribe to all state changes
44
+
45
+ ```bash
46
+ curl -N "http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events"
47
+ ```
48
+
49
+ #### Subscribe to light changes only
50
+
51
+ ```bash
52
+ curl -N "http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events?domain=light"
53
+ ```
54
+
55
+ #### Subscribe to a specific entity
56
+
57
+ ```bash
58
+ curl -N "http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events?entity_id=sensor.temperature"
59
+ ```
60
+
61
+ #### Subscribe to multiple event types
62
+
63
+ ```bash
64
+ curl -N "http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events?event_types=state_changed,call_service"
65
+ ```
66
+
67
+ ## SSE Event Format
68
+
69
+ ### Connection Established
70
+
71
+ When the connection is established, you'll receive a `connected` event:
72
+
73
+ ```
74
+ event: connected
75
+ data: {"client_id":"sse_1234567890_abc123","message":"Connected to Home Assistant event stream","filters":{"domain":"light"},"timestamp":"2026-01-09T12:00:00.000Z"}
76
+ ```
77
+
78
+ ### State Changed Events
79
+
80
+ ```
81
+ event: state_changed
82
+ data: {
83
+ "event_type": "state_changed",
84
+ "entity_id": "light.living_room",
85
+ "old_state": {
86
+ "entity_id": "light.living_room",
87
+ "state": "off",
88
+ "attributes": {"brightness": 0},
89
+ "last_changed": "2026-01-09T11:00:00.000Z",
90
+ "last_updated": "2026-01-09T11:00:00.000Z"
91
+ },
92
+ "new_state": {
93
+ "entity_id": "light.living_room",
94
+ "state": "on",
95
+ "attributes": {"brightness": 255},
96
+ "last_changed": "2026-01-09T12:00:00.000Z",
97
+ "last_updated": "2026-01-09T12:00:00.000Z"
98
+ },
99
+ "time_fired": "2026-01-09T12:00:00.000Z",
100
+ "context": {
101
+ "id": "abc123",
102
+ "parent_id": null,
103
+ "user_id": null
104
+ }
105
+ }
106
+ ```
107
+
108
+ ### Service Call Events
109
+
110
+ ```
111
+ event: call_service
112
+ data: {
113
+ "event_type": "call_service",
114
+ "domain": "light",
115
+ "service": "turn_on",
116
+ "service_data": {"brightness": 255},
117
+ "time_fired": "2026-01-09T12:00:00.000Z"
118
+ }
119
+ ```
120
+
121
+ ### Keep-Alive Comments
122
+
123
+ The server sends periodic keep-alive comments to maintain the connection:
124
+
125
+ ```
126
+ : keepalive 2026-01-09T12:00:30.000Z
127
+ ```
128
+
129
+ ### Error Events
130
+
131
+ ```
132
+ event: error
133
+ data: {"error":"Connection lost","message":"WebSocket disconnected"}
134
+ ```
135
+
136
+ ### Disconnect Events
137
+
138
+ ```
139
+ event: disconnect
140
+ data: {"reason":"Server shutdown"}
141
+ ```
142
+
143
+ ## JavaScript Client Example
144
+
145
+ ### Basic Usage
146
+
147
+ ```javascript
148
+ const eventSource = new EventSource(
149
+ 'http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events?domain=light'
150
+ );
151
+
152
+ // Handle connection
153
+ eventSource.addEventListener('connected', (event) => {
154
+ const data = JSON.parse(event.data);
155
+ console.log('Connected:', data.client_id);
156
+ });
157
+
158
+ // Handle state changes
159
+ eventSource.addEventListener('state_changed', (event) => {
160
+ const data = JSON.parse(event.data);
161
+ console.log('State changed:', data.entity_id);
162
+ console.log(' Old state:', data.old_state?.state);
163
+ console.log(' New state:', data.new_state?.state);
164
+ });
165
+
166
+ // Handle errors
167
+ eventSource.onerror = (error) => {
168
+ console.error('SSE error:', error);
169
+ // EventSource will automatically reconnect
170
+ };
171
+
172
+ // Close connection when done
173
+ // eventSource.close();
174
+ ```
175
+
176
+ ### React Hook Example
177
+
178
+ ```javascript
179
+ import { useEffect, useState } from 'react';
180
+
181
+ function useHomeAssistantEvents(domain) {
182
+ const [events, setEvents] = useState([]);
183
+ const [connected, setConnected] = useState(false);
184
+
185
+ useEffect(() => {
186
+ const url = domain
187
+ ? `http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events?domain=${domain}`
188
+ : 'http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events';
189
+
190
+ const eventSource = new EventSource(url);
191
+
192
+ eventSource.addEventListener('connected', () => {
193
+ setConnected(true);
194
+ });
195
+
196
+ eventSource.addEventListener('state_changed', (event) => {
197
+ const data = JSON.parse(event.data);
198
+ setEvents(prev => [...prev.slice(-99), data]);
199
+ });
200
+
201
+ eventSource.onerror = () => {
202
+ setConnected(false);
203
+ };
204
+
205
+ return () => {
206
+ eventSource.close();
207
+ };
208
+ }, [domain]);
209
+
210
+ return { events, connected };
211
+ }
212
+
213
+ // Usage in component
214
+ function LightMonitor() {
215
+ const { events, connected } = useHomeAssistantEvents('light');
216
+
217
+ return (
218
+ <div>
219
+ <p>Status: {connected ? 'Connected' : 'Disconnected'}</p>
220
+ <ul>
221
+ {events.map((event, i) => (
222
+ <li key={i}>
223
+ {event.entity_id}: {event.new_state?.state}
224
+ </li>
225
+ ))}
226
+ </ul>
227
+ </div>
228
+ );
229
+ }
230
+ ```
231
+
232
+ ### Node.js Client Example
233
+
234
+ ```javascript
235
+ import EventSource from 'eventsource';
236
+
237
+ const es = new EventSource('http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events');
238
+
239
+ es.addEventListener('connected', (event) => {
240
+ console.log('Connected to Home Assistant');
241
+ });
242
+
243
+ es.addEventListener('state_changed', (event) => {
244
+ const data = JSON.parse(event.data);
245
+ console.log(`${data.entity_id}: ${data.old_state?.state} -> ${data.new_state?.state}`);
246
+ });
247
+
248
+ es.onerror = (error) => {
249
+ console.error('Error:', error);
250
+ };
251
+ ```
252
+
253
+ ## Event Types
254
+
255
+ | Event Type | Description |
256
+ |------------|-------------|
257
+ | `state_changed` | Entity state has changed |
258
+ | `call_service` | A service was called |
259
+ | `automation_triggered` | An automation was triggered |
260
+ | `script_started` | A script was started |
261
+ | `homeassistant_start` | Home Assistant has started |
262
+ | `homeassistant_stop` | Home Assistant is stopping |
263
+
264
+ ## Health Check
265
+
266
+ The health endpoint includes SSE-related information:
267
+
268
+ ```bash
269
+ curl http://mcpserver.10.0.0.18.nip.io:3000/health
270
+ ```
271
+
272
+ Response:
273
+ ```json
274
+ {
275
+ "status": "healthy",
276
+ "version": "0.5.0",
277
+ "sseEventsEnabled": true,
278
+ "sseEventsPath": "/subscribe_events",
279
+ "sseConnectedClients": 3
280
+ }
281
+ ```
282
+
283
+ ## Rate Limiting
284
+
285
+ The SSE endpoint is subject to the same rate limiting as other endpoints:
286
+
287
+ - Default: 100 requests per minute per IP
288
+ - Configurable via `MCP_RATE_LIMIT_MAX_REQUESTS` and `MCP_RATE_LIMIT_WINDOW_MS`
289
+
290
+ Note: Once an SSE connection is established, it remains open and does not count against rate limits.
291
+
292
+ ## CORS Support
293
+
294
+ CORS is enabled for the SSE endpoint to allow browser-based clients:
295
+
296
+ ```javascript
297
+ // Browser clients from any origin can connect
298
+ const eventSource = new EventSource(
299
+ 'http://mcpserver.10.0.0.18.nip.io:3000/subscribe_events'
300
+ );
301
+ ```
302
+
303
+ ## Troubleshooting
304
+
305
+ ### Connection keeps disconnecting
306
+
307
+ - Check that your Home Assistant instance is accessible
308
+ - Verify the `HA_URL` and `HA_TOKEN` are correct
309
+ - Check the server logs for WebSocket errors
310
+
311
+ ### No events received
312
+
313
+ - Verify that events are actually occurring in Home Assistant
314
+ - Check that your domain/entity filters are correct
315
+ - Try subscribing without filters first
316
+
317
+ ### CORS errors in browser
318
+
319
+ - Ensure `MCP_HTTP_ALLOW_CORS=true` is set
320
+ - Check that your origin is in `MCP_HTTP_ALLOWED_ORIGINS` or use `*`
321
+
322
+ ### High memory usage
323
+
324
+ - Limit the number of concurrent SSE connections
325
+ - Use domain/entity filters to reduce event volume
326
+ - Consider using the rate limiter to prevent abuse
327
+
328
+ ## Best Practices
329
+
330
+ 1. **Use filters**: Subscribe only to the events you need to reduce bandwidth and processing
331
+ 2. **Handle reconnection**: EventSource automatically reconnects, but handle the reconnection gracefully
332
+ 3. **Debounce updates**: If updating UI frequently, consider debouncing to prevent performance issues
333
+ 4. **Clean up**: Always close the EventSource when the component unmounts or when done
334
+ 5. **Error handling**: Implement proper error handling for network issues
335
+
336
+ ## Security Considerations
337
+
338
+ 1. The SSE endpoint does not currently require authentication (relies on network security)
339
+ 2. Run the server on a private network or behind a reverse proxy with authentication
340
+ 3. Use HTTPS in production to encrypt event data
341
+ 4. Consider using the `token` parameter for future authentication implementations
@@ -0,0 +1,33 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+
4
+ export default tseslint.config(
5
+ eslint.configs.recommended,
6
+ ...tseslint.configs.recommended,
7
+ {
8
+ ignores: ['dist/**', 'node_modules/**', 'coverage/**', 'example/**'],
9
+ },
10
+ {
11
+ files: ['src/**/*.ts'],
12
+ languageOptions: {
13
+ parserOptions: {
14
+ projectService: true,
15
+ tsconfigRootDir: import.meta.dirname,
16
+ },
17
+ },
18
+ rules: {
19
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
20
+ '@typescript-eslint/explicit-function-return-type': 'off',
21
+ '@typescript-eslint/no-explicit-any': 'off',
22
+ },
23
+ },
24
+ {
25
+ files: ['tests/**/*.ts'],
26
+ rules: {
27
+ '@typescript-eslint/no-unused-vars': 'off',
28
+ '@typescript-eslint/explicit-function-return-type': 'off',
29
+ '@typescript-eslint/no-explicit-any': 'off',
30
+ 'prefer-const': 'off',
31
+ },
32
+ }
33
+ );