@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.
- package/.dockerignore +8 -0
- package/.env.example +149 -0
- package/.gitattributes +30 -0
- package/Dockerfile +33 -0
- package/LICENSE +21 -0
- package/README.md +394 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +183 -0
- package/dist/config.js.map +1 -0
- package/dist/haClient/automations.d.ts +67 -0
- package/dist/haClient/automations.d.ts.map +1 -0
- package/dist/haClient/automations.js +181 -0
- package/dist/haClient/automations.js.map +1 -0
- package/dist/haClient/config.d.ts +28 -0
- package/dist/haClient/config.d.ts.map +1 -0
- package/dist/haClient/config.js +38 -0
- package/dist/haClient/config.js.map +1 -0
- package/dist/haClient/devices.d.ts +219 -0
- package/dist/haClient/devices.d.ts.map +1 -0
- package/dist/haClient/devices.js +506 -0
- package/dist/haClient/devices.js.map +1 -0
- package/dist/haClient/entities.d.ts +35 -0
- package/dist/haClient/entities.d.ts.map +1 -0
- package/dist/haClient/entities.js +97 -0
- package/dist/haClient/entities.js.map +1 -0
- package/dist/haClient/events.d.ts +138 -0
- package/dist/haClient/events.d.ts.map +1 -0
- package/dist/haClient/events.js +344 -0
- package/dist/haClient/events.js.map +1 -0
- package/dist/haClient/history.d.ts +26 -0
- package/dist/haClient/history.d.ts.map +1 -0
- package/dist/haClient/history.js +54 -0
- package/dist/haClient/history.js.map +1 -0
- package/dist/haClient/index.d.ts +153 -0
- package/dist/haClient/index.d.ts.map +1 -0
- package/dist/haClient/index.js +223 -0
- package/dist/haClient/index.js.map +1 -0
- package/dist/haClient/request.d.ts +34 -0
- package/dist/haClient/request.d.ts.map +1 -0
- package/dist/haClient/request.js +93 -0
- package/dist/haClient/request.js.map +1 -0
- package/dist/haClient/services.d.ts +22 -0
- package/dist/haClient/services.d.ts.map +1 -0
- package/dist/haClient/services.js +39 -0
- package/dist/haClient/services.js.map +1 -0
- package/dist/haClient/states.d.ts +26 -0
- package/dist/haClient/states.d.ts.map +1 -0
- package/dist/haClient/states.js +53 -0
- package/dist/haClient/states.js.map +1 -0
- package/dist/haClient/updates.d.ts +22 -0
- package/dist/haClient/updates.d.ts.map +1 -0
- package/dist/haClient/updates.js +48 -0
- package/dist/haClient/updates.js.map +1 -0
- package/dist/haClient.d.ts +97 -0
- package/dist/haClient.d.ts.map +1 -0
- package/dist/haClient.js +344 -0
- package/dist/haClient.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/localAI/index.d.ts +37 -0
- package/dist/localAI/index.d.ts.map +1 -0
- package/dist/localAI/index.js +73 -0
- package/dist/localAI/index.js.map +1 -0
- package/dist/localAI/prompts.d.ts +20 -0
- package/dist/localAI/prompts.d.ts.map +1 -0
- package/dist/localAI/prompts.js +48 -0
- package/dist/localAI/prompts.js.map +1 -0
- package/dist/localAI/providers/base.d.ts +51 -0
- package/dist/localAI/providers/base.d.ts.map +1 -0
- package/dist/localAI/providers/base.js +80 -0
- package/dist/localAI/providers/base.js.map +1 -0
- package/dist/localAI/providers/ollama.d.ts +22 -0
- package/dist/localAI/providers/ollama.d.ts.map +1 -0
- package/dist/localAI/providers/ollama.js +63 -0
- package/dist/localAI/providers/ollama.js.map +1 -0
- package/dist/localAI/providers/openai.d.ts +23 -0
- package/dist/localAI/providers/openai.d.ts.map +1 -0
- package/dist/localAI/providers/openai.js +63 -0
- package/dist/localAI/providers/openai.js.map +1 -0
- package/dist/localAI/types.d.ts +61 -0
- package/dist/localAI/types.d.ts.map +1 -0
- package/dist/localAI/types.js +6 -0
- package/dist/localAI/types.js.map +1 -0
- package/dist/ollamaClient.d.ts +37 -0
- package/dist/ollamaClient.d.ts.map +1 -0
- package/dist/ollamaClient.js +110 -0
- package/dist/ollamaClient.js.map +1 -0
- package/dist/resources/index.d.ts +11 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +238 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/server/common.d.ts +9 -0
- package/dist/server/common.d.ts.map +1 -0
- package/dist/server/common.js +27 -0
- package/dist/server/common.js.map +1 -0
- package/dist/server/eventSubscription.d.ts +53 -0
- package/dist/server/eventSubscription.d.ts.map +1 -0
- package/dist/server/eventSubscription.js +257 -0
- package/dist/server/eventSubscription.js.map +1 -0
- package/dist/server/http.d.ts +12 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +491 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/rateLimiter.d.ts +70 -0
- package/dist/server/rateLimiter.d.ts.map +1 -0
- package/dist/server/rateLimiter.js +156 -0
- package/dist/server/rateLimiter.js.map +1 -0
- package/dist/server/sse.d.ts +32 -0
- package/dist/server/sse.d.ts.map +1 -0
- package/dist/server/sse.js +76 -0
- package/dist/server/sse.js.map +1 -0
- package/dist/server/stdio.d.ts +8 -0
- package/dist/server/stdio.d.ts.map +1 -0
- package/dist/server/stdio.js +16 -0
- package/dist/server/stdio.js.map +1 -0
- package/dist/server/stream.d.ts +26 -0
- package/dist/server/stream.d.ts.map +1 -0
- package/dist/server/stream.js +81 -0
- package/dist/server/stream.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +30 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/callService.d.ts +8 -0
- package/dist/tools/callService.d.ts.map +1 -0
- package/dist/tools/callService.js +100 -0
- package/dist/tools/callService.js.map +1 -0
- package/dist/tools/getClient.d.ts +8 -0
- package/dist/tools/getClient.d.ts.map +1 -0
- package/dist/tools/getClient.js +94 -0
- package/dist/tools/getClient.js.map +1 -0
- package/dist/tools/getDevice.d.ts +8 -0
- package/dist/tools/getDevice.d.ts.map +1 -0
- package/dist/tools/getDevice.js +94 -0
- package/dist/tools/getDevice.js.map +1 -0
- package/dist/tools/getEntitiesByDomain.d.ts +8 -0
- package/dist/tools/getEntitiesByDomain.d.ts.map +1 -0
- package/dist/tools/getEntitiesByDomain.js +80 -0
- package/dist/tools/getEntitiesByDomain.js.map +1 -0
- package/dist/tools/getState.d.ts +8 -0
- package/dist/tools/getState.d.ts.map +1 -0
- package/dist/tools/getState.js +90 -0
- package/dist/tools/getState.js.map +1 -0
- package/dist/tools/getStates.d.ts +8 -0
- package/dist/tools/getStates.d.ts.map +1 -0
- package/dist/tools/getStates.js +70 -0
- package/dist/tools/getStates.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/listClients.d.ts +8 -0
- package/dist/tools/listClients.d.ts.map +1 -0
- package/dist/tools/listClients.js +73 -0
- package/dist/tools/listClients.js.map +1 -0
- package/dist/tools/listDevices.d.ts +8 -0
- package/dist/tools/listDevices.d.ts.map +1 -0
- package/dist/tools/listDevices.js +73 -0
- package/dist/tools/listDevices.js.map +1 -0
- package/dist/tools/listSites.d.ts +8 -0
- package/dist/tools/listSites.d.ts.map +1 -0
- package/dist/tools/listSites.js +67 -0
- package/dist/tools/listSites.js.map +1 -0
- package/dist/tools/registry.d.ts +9 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +1382 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/searchDevices.d.ts +8 -0
- package/dist/tools/searchDevices.d.ts.map +1 -0
- package/dist/tools/searchDevices.js +84 -0
- package/dist/tools/searchDevices.js.map +1 -0
- package/dist/tools/searchEntities.d.ts +8 -0
- package/dist/tools/searchEntities.d.ts.map +1 -0
- package/dist/tools/searchEntities.js +80 -0
- package/dist/tools/searchEntities.js.map +1 -0
- package/dist/types/entity.d.ts +98 -0
- package/dist/types/entity.d.ts.map +1 -0
- package/dist/types/entity.js +2 -0
- package/dist/types/entity.js.map +1 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +32 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/haConfig.d.ts +8 -0
- package/dist/types/haConfig.d.ts.map +1 -0
- package/dist/types/haConfig.js +2 -0
- package/dist/types/haConfig.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/request.d.ts +7 -0
- package/dist/types/request.d.ts.map +1 -0
- package/dist/types/request.js +2 -0
- package/dist/types/request.js.map +1 -0
- package/dist/types/serviceCall.d.ts +18 -0
- package/dist/types/serviceCall.d.ts.map +1 -0
- package/dist/types/serviceCall.js +2 -0
- package/dist/types/serviceCall.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config-validations.d.ts +12 -0
- package/dist/utils/config-validations.d.ts.map +1 -0
- package/dist/utils/config-validations.js +43 -0
- package/dist/utils/config-validations.js.map +1 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +94 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/wrapper.d.ts +8 -0
- package/dist/wrapper.d.ts.map +1 -0
- package/dist/wrapper.js +301 -0
- package/dist/wrapper.js.map +1 -0
- package/docker-compose.yml +17 -0
- package/docs/CHANGELOG.md +86 -0
- package/docs/LOCAL_CLIENT.md +268 -0
- package/docs/SSE_API.md +341 -0
- package/eslint.config.js +33 -0
- package/n8n/ai-agent-safety-monitor.json +355 -0
- package/openapi.json +166 -0
- package/package.json +51 -0
- package/src/config.ts +260 -0
- package/src/haClient/automations.ts +217 -0
- package/src/haClient/config.ts +41 -0
- package/src/haClient/devices.ts +653 -0
- package/src/haClient/entities.ts +128 -0
- package/src/haClient/events.ts +456 -0
- package/src/haClient/history.ts +68 -0
- package/src/haClient/index.ts +287 -0
- package/src/haClient/request.ts +127 -0
- package/src/haClient/services.ts +45 -0
- package/src/haClient/states.ts +60 -0
- package/src/haClient/updates.ts +58 -0
- package/src/index.ts +99 -0
- package/src/localAI/index.ts +88 -0
- package/src/localAI/prompts.ts +50 -0
- package/src/localAI/providers/base.ts +120 -0
- package/src/localAI/providers/ollama.ts +78 -0
- package/src/localAI/providers/openai.ts +86 -0
- package/src/localAI/types.ts +67 -0
- package/src/resources/index.ts +266 -0
- package/src/server/common.ts +38 -0
- package/src/server/eventSubscription.ts +318 -0
- package/src/server/http.ts +546 -0
- package/src/server/rateLimiter.ts +195 -0
- package/src/server/sse.ts +120 -0
- package/src/server/stdio.ts +22 -0
- package/src/server/stream.ts +113 -0
- package/src/tools/callService.ts +114 -0
- package/src/tools/getEntitiesByDomain.ts +92 -0
- package/src/tools/getState.ts +103 -0
- package/src/tools/getStates.ts +80 -0
- package/src/tools/index.ts +9 -0
- package/src/tools/registry.ts +1521 -0
- package/src/tools/searchEntities.ts +92 -0
- package/src/types/entity.ts +110 -0
- package/src/types/errors.ts +33 -0
- package/src/types/haConfig.ts +7 -0
- package/src/types/index.ts +5 -0
- package/src/types/request.ts +6 -0
- package/src/types/serviceCall.ts +18 -0
- package/src/types.ts +2 -0
- package/src/utils/config-validations.ts +46 -0
- package/src/utils/logger.ts +114 -0
- package/src/wrapper.ts +340 -0
- package/tests/config.test.ts +332 -0
- package/tests/haClient/automations.test.ts +133 -0
- package/tests/haClient/config.test.ts +96 -0
- package/tests/haClient/entities.test.ts +188 -0
- package/tests/haClient/history.test.ts +170 -0
- package/tests/haClient/index.test.ts +221 -0
- package/tests/haClient/request.test.ts +217 -0
- package/tests/haClient/services.test.ts +120 -0
- package/tests/haClient/states.test.ts +119 -0
- package/tests/haClient/updates.test.ts +193 -0
- package/tests/index.test.ts +164 -0
- package/tests/localAI/index.test.ts +168 -0
- package/tests/localAI/ollama.test.ts +187 -0
- package/tests/localAI/openai.test.ts +257 -0
- package/tests/server/common.test.ts +64 -0
- package/tests/server/http.test.ts +326 -0
- package/tests/server/sse.test.ts +240 -0
- package/tests/server/stdio.test.ts +102 -0
- package/tests/server/stream.test.ts +287 -0
- package/tests/tools/registry.test.ts +355 -0
- package/tests/utils/config-validations.test.ts +289 -0
- package/tests/utils/logger.test.ts +327 -0
- package/tsconfig.json +25 -0
- 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`
|
package/docs/SSE_API.md
ADDED
|
@@ -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
|
package/eslint.config.js
ADDED
|
@@ -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
|
+
);
|