@mp-consulting/homebridge-ai-kit 2.0.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 (118) hide show
  1. package/README.md +273 -0
  2. package/config.schema.json +131 -0
  3. package/dist/agent/run-agent.d.ts +61 -0
  4. package/dist/agent/run-agent.d.ts.map +1 -0
  5. package/dist/agent/run-agent.js +92 -0
  6. package/dist/agent/run-agent.js.map +1 -0
  7. package/dist/bin/homebridge-ai-kit.d.ts +3 -0
  8. package/dist/bin/homebridge-ai-kit.d.ts.map +1 -0
  9. package/dist/bin/homebridge-ai-kit.js +61 -0
  10. package/dist/bin/homebridge-ai-kit.js.map +1 -0
  11. package/dist/bin/homebridge-mcp-server.d.ts +3 -0
  12. package/dist/bin/homebridge-mcp-server.d.ts.map +1 -0
  13. package/dist/bin/homebridge-mcp-server.js +5 -0
  14. package/dist/bin/homebridge-mcp-server.js.map +1 -0
  15. package/dist/index.d.ts +9 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +12 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/mcp/create-server.d.ts +17 -0
  20. package/dist/mcp/create-server.d.ts.map +1 -0
  21. package/dist/mcp/create-server.js +42 -0
  22. package/dist/mcp/create-server.js.map +1 -0
  23. package/dist/mcp/homebridge-client.d.ts +105 -0
  24. package/dist/mcp/homebridge-client.d.ts.map +1 -0
  25. package/dist/mcp/homebridge-client.js +341 -0
  26. package/dist/mcp/homebridge-client.js.map +1 -0
  27. package/dist/mcp/http.d.ts +26 -0
  28. package/dist/mcp/http.d.ts.map +1 -0
  29. package/dist/mcp/http.js +124 -0
  30. package/dist/mcp/http.js.map +1 -0
  31. package/dist/mcp/index.d.ts +12 -0
  32. package/dist/mcp/index.d.ts.map +1 -0
  33. package/dist/mcp/index.js +7 -0
  34. package/dist/mcp/index.js.map +1 -0
  35. package/dist/mcp/live.d.ts +46 -0
  36. package/dist/mcp/live.d.ts.map +1 -0
  37. package/dist/mcp/live.js +105 -0
  38. package/dist/mcp/live.js.map +1 -0
  39. package/dist/mcp/prompts.d.ts +4 -0
  40. package/dist/mcp/prompts.d.ts.map +1 -0
  41. package/dist/mcp/prompts.js +21 -0
  42. package/dist/mcp/prompts.js.map +1 -0
  43. package/dist/mcp/regex-search.d.ts +11 -0
  44. package/dist/mcp/regex-search.d.ts.map +1 -0
  45. package/dist/mcp/regex-search.js +53 -0
  46. package/dist/mcp/regex-search.js.map +1 -0
  47. package/dist/mcp/resources.d.ts +16 -0
  48. package/dist/mcp/resources.d.ts.map +1 -0
  49. package/dist/mcp/resources.js +65 -0
  50. package/dist/mcp/resources.js.map +1 -0
  51. package/dist/mcp/stdio.d.ts +11 -0
  52. package/dist/mcp/stdio.d.ts.map +1 -0
  53. package/dist/mcp/stdio.js +56 -0
  54. package/dist/mcp/stdio.js.map +1 -0
  55. package/dist/mcp/tools/accessories.d.ts +15 -0
  56. package/dist/mcp/tools/accessories.d.ts.map +1 -0
  57. package/dist/mcp/tools/accessories.js +179 -0
  58. package/dist/mcp/tools/accessories.js.map +1 -0
  59. package/dist/mcp/tools/child-bridges.d.ts +3 -0
  60. package/dist/mcp/tools/child-bridges.d.ts.map +1 -0
  61. package/dist/mcp/tools/child-bridges.js +28 -0
  62. package/dist/mcp/tools/child-bridges.js.map +1 -0
  63. package/dist/mcp/tools/config.d.ts +7 -0
  64. package/dist/mcp/tools/config.d.ts.map +1 -0
  65. package/dist/mcp/tools/config.js +111 -0
  66. package/dist/mcp/tools/config.js.map +1 -0
  67. package/dist/mcp/tools/helpers.d.ts +31 -0
  68. package/dist/mcp/tools/helpers.d.ts.map +1 -0
  69. package/dist/mcp/tools/helpers.js +49 -0
  70. package/dist/mcp/tools/helpers.js.map +1 -0
  71. package/dist/mcp/tools/logs.d.ts +7 -0
  72. package/dist/mcp/tools/logs.d.ts.map +1 -0
  73. package/dist/mcp/tools/logs.js +122 -0
  74. package/dist/mcp/tools/logs.js.map +1 -0
  75. package/dist/mcp/tools/plugin-jobs.d.ts +11 -0
  76. package/dist/mcp/tools/plugin-jobs.d.ts.map +1 -0
  77. package/dist/mcp/tools/plugin-jobs.js +75 -0
  78. package/dist/mcp/tools/plugin-jobs.js.map +1 -0
  79. package/dist/mcp/tools/plugins.d.ts +3 -0
  80. package/dist/mcp/tools/plugins.d.ts.map +1 -0
  81. package/dist/mcp/tools/plugins.js +74 -0
  82. package/dist/mcp/tools/plugins.js.map +1 -0
  83. package/dist/mcp/tools/server.d.ts +3 -0
  84. package/dist/mcp/tools/server.d.ts.map +1 -0
  85. package/dist/mcp/tools/server.js +69 -0
  86. package/dist/mcp/tools/server.js.map +1 -0
  87. package/dist/mcp/tools/system.d.ts +3 -0
  88. package/dist/mcp/tools/system.d.ts.map +1 -0
  89. package/dist/mcp/tools/system.js +9 -0
  90. package/dist/mcp/tools/system.js.map +1 -0
  91. package/dist/mcp/types.d.ts +75 -0
  92. package/dist/mcp/types.d.ts.map +1 -0
  93. package/dist/mcp/types.js +2 -0
  94. package/dist/mcp/types.js.map +1 -0
  95. package/dist/plugin/index.d.ts +7 -0
  96. package/dist/plugin/index.d.ts.map +1 -0
  97. package/dist/plugin/index.js +5 -0
  98. package/dist/plugin/index.js.map +1 -0
  99. package/dist/plugin/platform.d.ts +28 -0
  100. package/dist/plugin/platform.d.ts.map +1 -0
  101. package/dist/plugin/platform.js +76 -0
  102. package/dist/plugin/platform.js.map +1 -0
  103. package/dist/plugin/snippets.d.ts +27 -0
  104. package/dist/plugin/snippets.d.ts.map +1 -0
  105. package/dist/plugin/snippets.js +33 -0
  106. package/dist/plugin/snippets.js.map +1 -0
  107. package/homebridge-ui/public/index.html +164 -0
  108. package/homebridge-ui/public/js/app.js +200 -0
  109. package/homebridge-ui/public/lib/ai.css +738 -0
  110. package/homebridge-ui/public/lib/bootstrap-icons.min.css +5 -0
  111. package/homebridge-ui/public/lib/bootstrap.bundle.min.js +6 -0
  112. package/homebridge-ui/public/lib/bootstrap.min.css +5 -0
  113. package/homebridge-ui/public/lib/fonts/bootstrap-icons.woff +0 -0
  114. package/homebridge-ui/public/lib/fonts/bootstrap-icons.woff2 +0 -0
  115. package/homebridge-ui/public/lib/kit.css +994 -0
  116. package/homebridge-ui/public/lib/kit.js +794 -0
  117. package/homebridge-ui/server.js +28 -0
  118. package/package.json +94 -0
package/README.md ADDED
@@ -0,0 +1,273 @@
1
+ # @mp-consulting/homebridge-ai-kit
2
+
3
+ AI toolkit for [Homebridge](https://homebridge.io), in one package:
4
+
5
+ - **An MCP server** that lets AI assistants (Claude Desktop, Claude Code, Cursor) control accessories, manage plugins, edit configuration and read logs, over stdio or Streamable HTTP.
6
+ - **The Assistant**: provider adapters (Claude, OpenAI, Gemini, any OpenAI-compatible server), an agent loop wired to the MCP tools, and ready-made features (log doctor, config copilot, device-error explainer, update-risk briefing, organiser, daily digest). Homebridge Glass UI and the MP Consulting plugins use these.
7
+ - **A Homebridge plugin** (`HomebridgeAiKit` platform) with a settings page to pick the provider and model, test the connection, serve MCP over HTTP and generate client configs.
8
+
9
+ The AI building blocks that don't need MCP also ship on their own as [`@mp-consulting/homebridge-ai-core`](packages/ai-core) — see [Packages](#packages).
10
+
11
+ > **Renamed from `@mp-consulting/homebridge-mcp-server`.** The old `homebridge-mcp-server` command still works, so existing MCP client configs don't need to change. See [Migrating from homebridge-mcp-server](#migrating-from-homebridge-mcp-server).
12
+
13
+ ## Contents
14
+
15
+ - [Packages](#packages)
16
+ - [Features](#features)
17
+ - [Providers](#providers)
18
+ - [Homebridge plugin](#homebridge-plugin)
19
+ - [MCP server](#mcp-server)
20
+ - [Library API](#library-api)
21
+ - [Security](#security)
22
+ - [Migrating from homebridge-mcp-server](#migrating-from-homebridge-mcp-server)
23
+ - [Development](#development)
24
+
25
+ ## Packages
26
+
27
+ This repository is an npm workspace that publishes two packages, both at version 2.0.0:
28
+
29
+ | Package | Use it for | Runtime dependencies |
30
+ |---|---|---|
31
+ | [`@mp-consulting/homebridge-ai-core`](packages/ai-core) | **Homebridge plugins.** The plugin-UI Assistant routes (`registerAiRoutes` from `./plugin`), providers, redaction, prompts, config and the Assistant features (everything except `runAgent`). | `ajv` only |
32
+ | `@mp-consulting/homebridge-ai-kit` (this package) | **Homebridge Glass UI and MCP.** Everything in ai-core (re-exported) plus the MCP server, `runAgent`, the `HomebridgeAiKit` Homebridge plugin and `mcpClientSnippets`. | ai-core, `@modelcontextprotocol/sdk`, `socket.io-client`, `zod`, `@homebridge/plugin-ui-utils` |
33
+
34
+ Plugins should depend on **ai-core**, so installing them doesn't pull in the MCP SDK, socket.io or zod. ai-kit stays backward compatible: its `.` and `./plugin` exports re-export everything ai-core has under the same names, so code that imports from ai-kit keeps working. For local development before ai-core is published, a plugin next to this repo can use `"@mp-consulting/homebridge-ai-core": "file:../homebridge-mcp-server/packages/ai-core"` (run `npm run build` here first, and switch to `^2.0.0` before releasing the plugin).
35
+
36
+ ## Features
37
+
38
+ **MCP tools** (31): accessories (list, get, control with value checks, room layout), server (status, restart, pairing, cached accessories), child bridges (list, restart, stop, start), config (read with secrets redacted, full write, partial `patch_config`), plugins (list, search, versions, schema, changelog, install, update, uninstall), system info and logs (recent lines, regex search).
39
+
40
+ **MCP resources** you can subscribe to: `homebridge://accessories`, `homebridge://logs/recent`, `homebridge://status`. Changes arrive over the Homebridge UI's socket.io namespaces, or by polling when the socket can't be used.
41
+
42
+ **MCP prompts**: `diagnose-logs`, `plan-upgrade`, `audit-config`.
43
+
44
+ **Assistant features** (library): `diagnoseLogs`, `generatePluginConfig`, `explainDeviceError`, `assessPluginUpdate`, `suggestOrganization`, `dailyDigest`, `ask`, and `runAgent` for anything that needs the tools. Every input is redacted before it reaches a provider and trimmed to fit its context window; JSON outputs are checked against a schema with one automatic repair attempt; token usage and Claude costs are tracked.
45
+
46
+ ## Providers
47
+
48
+ All providers use plain `fetch` (no SDKs). Each declares what it can do, and features without tool calling fall back to prompt-only answers.
49
+
50
+ | `provider` | Default model | API key | Tools | Streaming | Context |
51
+ |---|---|---|---|---|---|
52
+ | `anthropic` | `claude-sonnet-5-5` | required | yes | yes | 1M (200K for Haiku) |
53
+ | `openai` | `gpt-5` | required | yes | yes | 128K |
54
+ | `gemini` | `gemini-2.5-pro` | required | yes | yes | 1M |
55
+ | `openai-compatible` | `llama3.1` | optional | yes | yes | 8K (set `contextTokens`) |
56
+
57
+ - Claude models: `claude-sonnet-5-5` (default), `claude-haiku-4-5-20251001` (cheapest), `claude-opus-5-5` (most capable).
58
+ - `openai-compatible` works with Ollama (`http://127.0.0.1:11434/v1`, the default), LM Studio (`http://127.0.0.1:1234/v1`), vLLM and similar. Set `contextTokens` to your model's context window so inputs are trimmed correctly.
59
+ - An on-device Apple Foundation Models provider is planned.
60
+
61
+ ## Homebridge plugin
62
+
63
+ Install it like any plugin (Homebridge 1.8+ or 2.x):
64
+
65
+ ```bash
66
+ npm install -g @mp-consulting/homebridge-ai-kit
67
+ ```
68
+
69
+ Then open its settings in the Homebridge UI. The settings page edits the `HomebridgeAiKit` platform block, tests the connection with the values in the form, and shows ready-to-paste configs for Claude Desktop, Claude Code and Cursor.
70
+
71
+ ```jsonc
72
+ {
73
+ "platform": "HomebridgeAiKit",
74
+ "name": "AI Kit",
75
+ "enabled": true,
76
+ "provider": "anthropic", // anthropic | openai | gemini | openai-compatible
77
+ "model": "claude-sonnet-5-5", // optional, defaults per provider
78
+ "apiKey": "sk-ant-…", // secret, redacted wherever config is shown to a model
79
+ "baseUrl": "http://127.0.0.1:11434/v1", // openai-compatible only (or a proxy)
80
+ "contextTokens": 32768, // optional override, mainly for local models
81
+ "maxOutputTokens": 2048,
82
+ "mcp": {
83
+ "http": {
84
+ "enabled": false,
85
+ "host": "127.0.0.1",
86
+ "port": 8582,
87
+ "token": "…", // bearer token MCP clients must send
88
+ "homebridgeUrl": "http://127.0.0.1:8581",
89
+ "homebridgeToken": "hbg_…" // Glass UI API token the tools act with
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ With `mcp.http.enabled`, the plugin serves MCP at `http://<host>:<port>/mcp` while Homebridge runs. It needs a client token (`mcp.http.token` or `HOMEBRIDGE_AI_MCP_TOKEN`) and Homebridge credentials: a Glass UI API token in `homebridgeToken`, or the `HOMEBRIDGE_*` environment variables. A read-only API token gives clients read-only access.
96
+
97
+ ### Assistant routes for other plugins
98
+
99
+ A plugin's custom UI server can offer the Assistant with one call. Import the routes from `@mp-consulting/homebridge-ai-core/plugin` (ai-kit's `./plugin` re-exports them, but plugins should depend on the slimmer ai-core). The browser side is `MpKit.ai` from [`@mp-consulting/homebridge-ui-kit`](https://github.com/mp-consulting/homebridge-ui-kit):
100
+
101
+ ```js
102
+ // homebridge-ui/server.js
103
+ import { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils';
104
+ import { registerAiRoutes } from '@mp-consulting/homebridge-ai-core/plugin';
105
+
106
+ class UiServer extends HomebridgePluginUiServer {
107
+ constructor() {
108
+ super();
109
+ registerAiRoutes(this, { pluginName: '@mp-consulting/homebridge-ewelink' });
110
+ this.ready();
111
+ }
112
+ }
113
+ new UiServer();
114
+ ```
115
+
116
+ | Route | Body | Result |
117
+ |---|---|---|
118
+ | `/ai/status` | none | `{ enabled, provider, model, capabilities }` (never the key) |
119
+ | `/ai/explain` | `{ error, context?, device?, requestId? }` | `{ text, usage }` |
120
+ | `/ai/ask` | `{ prompt, context?, requestId? }` | `{ text, usage }` |
121
+ | `/ai/config` | `{ schema, request, current?, requestId? }` | `{ config, explanation, usage }` |
122
+
123
+ With a `requestId`, the server streams `ai:chunk` `{ requestId, delta }` events, then `ai:done` `{ requestId }` or `ai:error` `{ requestId, message }`. The routes read the `HomebridgeAiKit` block from config.json on every request, so settings changes apply at once.
124
+
125
+ ## MCP server
126
+
127
+ ### Environment
128
+
129
+ | Variable | Description |
130
+ |----------|-------------|
131
+ | `HOMEBRIDGE_URL` | URL of your Homebridge UI, e.g. `http://192.168.1.100:8581` (required) |
132
+ | `HOMEBRIDGE_TOKEN` | A Homebridge UI API token (Glass UI `hbg_…`). Replaces username and password |
133
+ | `HOMEBRIDGE_USERNAME` / `HOMEBRIDGE_PASSWORD` | UI login, when no token is set |
134
+ | `HOMEBRIDGE_READ_ONLY` | `true` removes every tool that changes something |
135
+ | `HOMEBRIDGE_TIMEOUT_MS` | Request timeout (default `30000`) |
136
+ | `HOMEBRIDGE_AI_MCP_TOKEN` | Bearer token required by `--http` |
137
+
138
+ ### stdio (Claude Desktop, Claude Code, Cursor)
139
+
140
+ ```json
141
+ {
142
+ "mcpServers": {
143
+ "homebridge": {
144
+ "command": "npx",
145
+ "args": ["-y", "@mp-consulting/homebridge-ai-kit", "mcp"],
146
+ "env": {
147
+ "HOMEBRIDGE_URL": "http://192.168.1.100:8581",
148
+ "HOMEBRIDGE_TOKEN": "hbg_…"
149
+ }
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ ```bash
156
+ claude mcp add homebridge -e HOMEBRIDGE_URL=http://192.168.1.100:8581 -e HOMEBRIDGE_TOKEN=hbg_… -- npx -y @mp-consulting/homebridge-ai-kit mcp
157
+ ```
158
+
159
+ ### Streamable HTTP
160
+
161
+ ```bash
162
+ HOMEBRIDGE_AI_MCP_TOKEN=$(openssl rand -base64 24) \
163
+ HOMEBRIDGE_URL=http://127.0.0.1:8581 HOMEBRIDGE_TOKEN=hbg_… \
164
+ homebridge-ai-kit mcp --http --port 8582 --host 127.0.0.1
165
+ ```
166
+
167
+ The endpoint is `http://127.0.0.1:8582/mcp`. Every request needs `Authorization: Bearer <HOMEBRIDGE_AI_MCP_TOKEN>`. It binds to `127.0.0.1` by default; only use `--host 0.0.0.0` on a trusted network, ideally behind HTTPS.
168
+
169
+ ```bash
170
+ claude mcp add --transport http homebridge http://127.0.0.1:8582/mcp --header "Authorization: Bearer <token>"
171
+ ```
172
+
173
+ ### Tools
174
+
175
+ | Group | Tools |
176
+ |---|---|
177
+ | Accessories | `list_accessories` (filter by `room`, `type`, `name`, `manufacturer`, `excludeManufacturer`), `get_accessory`, `set_accessory`, `get_accessory_layout` |
178
+ | Server | `get_homebridge_status`, `get_server_status`, `restart_homebridge`, `get_pairing_info`, `get_cached_accessories`, `remove_cached_accessory`, `reset_cached_accessories` |
179
+ | Child bridges | `list_child_bridges`, `restart_child_bridge`, `stop_child_bridge`, `start_child_bridge` |
180
+ | Config | `get_config`, `update_config`, `patch_config` |
181
+ | Plugins | `list_plugins`, `search_plugins`, `lookup_plugin`, `get_plugin_versions`, `get_plugin_config_schema`, `get_plugin_changelog`, `install_plugin`, `update_plugin`, `uninstall_plugin`, `get_plugin_job` |
182
+ | System | `get_system_info` |
183
+ | Logs | `get_recent_logs`, `search_logs` |
184
+
185
+ - `set_accessory` checks the value against the characteristic first (format, min/max, step, valid values, write permission), coerces `"50"` to `50` or `1` to `true`, and explains what is wrong instead of sending a bad value.
186
+ - `patch_config` changes one platform or accessory block (found by `platform`/`accessory` plus `name`); objects merge, `null` removes a key, and `__REDACTED__` keeps the current secret.
187
+ - `install_plugin`, `update_plugin` and `uninstall_plugin` start a job on the Homebridge UI and wait up to two minutes for it; `get_plugin_job` follows a longer one. They need Homebridge Glass UI (`POST /api/plugins/install|update|uninstall`, `GET /api/plugins/jobs/:id`).
188
+ - The log tools need a Homebridge install managed by [hb-service](https://github.com/homebridge/homebridge-config-ui-x/wiki/Homebridge-Service-Command).
189
+
190
+ ## Library API
191
+
192
+ ```js
193
+ import {
194
+ createProvider, readAiConfig, runAgent, diagnoseLogs, generatePluginConfig, UsageTracker,
195
+ } from '@mp-consulting/homebridge-ai-kit';
196
+ import { HomebridgeClient } from '@mp-consulting/homebridge-ai-kit/mcp';
197
+
198
+ const config = await readAiConfig(); // HomebridgeAiKit block of ~/.homebridge/config.json
199
+ const provider = createProvider(config);
200
+
201
+ // One-shot feature, streamed
202
+ const { text } = await diagnoseLogs({ provider, logs, onChunk: (d) => process.stdout.write(d) });
203
+
204
+ // Agent with the MCP tools, acting as the current user
205
+ const client = new HomebridgeClient({ url: 'http://127.0.0.1:8581', getToken: () => mintShortLivedToken(user) });
206
+ const result = await runAgent({
207
+ provider,
208
+ client,
209
+ messages: [{ role: 'user', content: 'Turn off every light downstairs' }],
210
+ confirm: async (call) => askUser(`Allow ${call.name}?`), // destructive tools are refused without it
211
+ onEvent: (e) => console.log(e),
212
+ });
213
+ ```
214
+
215
+ | Export | Purpose |
216
+ |---|---|
217
+ | `createProvider(config)` → `AiProvider` | `chat(req)` and `stream(req)` with `ChatRequest { system?, messages, tools?, maxOutputTokens?, signal? }` |
218
+ | `runAgent(opts)` → `AgentResult` | Loops model ↔ MCP tools (in-memory transport), `maxSteps` 8 by default, `readOnly`, `confirm` for destructive tools |
219
+ | `diagnoseLogs`, `generatePluginConfig`, `explainDeviceError`, `assessPluginUpdate`, `suggestOrganization`, `dailyDigest`, `ask` | Ready-made features; all accept `onChunk`, `signal`, `systemContext` |
220
+ | `generateJson({ provider, schema, prompt })` | Schema-checked JSON (ajv) with one repair retry |
221
+ | `trimToContext`, `estimateTokens` | Keep inputs inside the context window (logs keep their tail) |
222
+ | `UsageTracker`, `costOf` | Token and cost accounting (Claude prices; unknown models cost `null`) |
223
+ | `PROMPTS` | Prompt templates, shared with the MCP prompts |
224
+ | `readAiConfig`, `resolveAiConfig` | Read and default the `HomebridgeAiKit` block |
225
+ | `redactSecrets`, `restoreSecrets`, `redactText` | Keep credentials out of model context |
226
+
227
+ Everything in this table except `runAgent` comes from `@mp-consulting/homebridge-ai-core` and is re-exported here unchanged.
228
+
229
+ `./mcp` exports `createServer`, `HomebridgeClient`, `runStdioServer`, `runHttpServer`, `createLiveSource`; `./plugin` exports `registerAiRoutes` and `testAiConnection` (from ai-core's `./plugin`), `mcpClientSnippets` and `AiKitPlatform`.
230
+
231
+ ## Security
232
+
233
+ - **Secrets stay out of the model's context.** `get_config`, `patch_config` and the Assistant features replace passwords, tokens, API keys (including the AI Kit `apiKey` and MCP tokens) and the bridge pin with `__REDACTED__`. Writes swap the placeholders back for the real values. Free text sent to a provider (logs, errors) has credential-shaped values masked too.
234
+ - **Destructive actions need consent.** Every tool declares MCP `readOnlyHint` / `destructiveHint`; `runAgent` refuses destructive tools unless a `confirm` callback allows them. `HOMEBRIDGE_READ_ONLY=true` removes write tools entirely.
235
+ - **HTTP is locked down.** The HTTP transport requires a bearer token, compares it in constant time and binds to `127.0.0.1` by default.
236
+ - **`update_config` rejects incomplete configs**, and regex log searches run in a worker thread that is killed after 5 seconds.
237
+ - The server warns if `HOMEBRIDGE_URL` sends credentials over plain `http` to a non-local host.
238
+
239
+ ## Migrating from homebridge-mcp-server
240
+
241
+ ```bash
242
+ npm uninstall -g @mp-consulting/homebridge-mcp-server
243
+ npm install -g @mp-consulting/homebridge-ai-kit
244
+ ```
245
+
246
+ The package installs both `homebridge-ai-kit` and a `homebridge-mcp-server` alias, and the environment variables are unchanged, so existing configs keep working. New configs should use `homebridge-ai-kit mcp`. The MCP server now reports its name as `homebridge-ai-kit`, and library users import `createServer` and `HomebridgeClient` from `@mp-consulting/homebridge-ai-kit/mcp`.
247
+
248
+ ## Development
249
+
250
+ ```bash
251
+ git clone https://github.com/mp-consulting/homebridge-ai-kit.git
252
+ cd homebridge-ai-kit
253
+ npm install
254
+ npm run build # builds packages/ai-core, copies the ui-kit assets into homebridge-ui/public/lib, then tsc
255
+ ```
256
+
257
+ The repository is an npm workspace: the root is ai-kit and `packages/ai-core` is ai-core. The root scripts run both packages (ai-core first); `npm run <script> -w packages/ai-core` runs one script for ai-core alone. ai-kit's tests resolve `@mp-consulting/homebridge-ai-core` to its sources, while `typecheck` and `build` use ai-core's `dist`, so they build it first.
258
+
259
+ ```bash
260
+ npm run dev # MCP server on stdio with tsx
261
+ npm test # Run tests
262
+ npm run test:coverage # Tests with coverage thresholds (as CI does)
263
+ npm run lint # ESLint
264
+ npm run typecheck # Type-check src and test (both packages)
265
+ ```
266
+
267
+ **Publishing.** The release workflow publishes `@mp-consulting/homebridge-ai-core` first (skipped if that version is already on npm), then `@mp-consulting/homebridge-ai-kit`, which depends on it. Both use npm trusted publishing (OIDC): before the first release, configure a trusted publisher on npmjs.com for the new `@mp-consulting/homebridge-ai-core` package name too (repository `mp-consulting/homebridge-ai-kit`, workflow `publish.yml`); if npm only lets you add one to an existing package, publish ai-core 2.0.0 once by hand from `packages/ai-core`. The first ai-core release has to go out before ai-kit 2.0.0 can be installed from npm.
268
+
269
+ > **Before release:** `@mp-consulting/homebridge-ui-kit` is a `file:../homebridge-ui-kit` dev dependency while ui-kit 1.2 is unpublished. Switch it to `^1.2.0` before publishing; until then `npm ci` needs the ui-kit checkout next to this repo.
270
+
271
+ ## License
272
+
273
+ MIT
@@ -0,0 +1,131 @@
1
+ {
2
+ "pluginAlias": "HomebridgeAiKit",
3
+ "pluginType": "platform",
4
+ "singular": true,
5
+ "customUi": true,
6
+ "headerDisplay": "The Assistant: AI features for Homebridge and MP Consulting plugins, plus an MCP server for Claude Desktop, Claude Code and Cursor.",
7
+ "footerDisplay": "API keys stay on this Homebridge server and are never sent to the browser-side plugin pages.",
8
+ "schema": {
9
+ "type": "object",
10
+ "properties": {
11
+ "name": {
12
+ "title": "Name",
13
+ "type": "string",
14
+ "default": "AI Kit",
15
+ "required": true
16
+ },
17
+ "enabled": {
18
+ "title": "Enable the Assistant",
19
+ "type": "boolean",
20
+ "default": true
21
+ },
22
+ "provider": {
23
+ "title": "Provider",
24
+ "type": "string",
25
+ "default": "anthropic",
26
+ "oneOf": [
27
+ { "title": "Claude (Anthropic)", "enum": ["anthropic"] },
28
+ { "title": "OpenAI", "enum": ["openai"] },
29
+ { "title": "Gemini (Google)", "enum": ["gemini"] },
30
+ { "title": "OpenAI-compatible server (Ollama, LM Studio)", "enum": ["openai-compatible"] }
31
+ ],
32
+ "required": true
33
+ },
34
+ "model": {
35
+ "title": "Model",
36
+ "type": "string",
37
+ "placeholder": "claude-sonnet-5-5",
38
+ "description": "Leave empty for the provider's default: claude-sonnet-5-5, gpt-5, gemini-2.5-pro, or llama3.1 for a local server."
39
+ },
40
+ "apiKey": {
41
+ "title": "API key",
42
+ "type": "string",
43
+ "x-sensitive": true,
44
+ "description": "Required for Claude, OpenAI and Gemini. Optional for a local server.",
45
+ "condition": {
46
+ "functionBody": "return model.provider !== 'openai-compatible' || !!model.apiKey;"
47
+ }
48
+ },
49
+ "baseUrl": {
50
+ "title": "Server URL",
51
+ "type": "string",
52
+ "format": "uri",
53
+ "placeholder": "http://127.0.0.1:11434/v1",
54
+ "description": "The OpenAI-compatible endpoint, e.g. Ollama http://127.0.0.1:11434/v1 or LM Studio http://127.0.0.1:1234/v1.",
55
+ "condition": {
56
+ "functionBody": "return model.provider === 'openai-compatible';"
57
+ }
58
+ },
59
+ "contextTokens": {
60
+ "title": "Context window (tokens)",
61
+ "type": "integer",
62
+ "minimum": 1024,
63
+ "placeholder": 8192,
64
+ "description": "How much text the local model can read at once. Inputs such as logs are trimmed to fit.",
65
+ "condition": {
66
+ "functionBody": "return model.provider === 'openai-compatible';"
67
+ }
68
+ },
69
+ "maxOutputTokens": {
70
+ "title": "Maximum answer length (tokens)",
71
+ "type": "integer",
72
+ "minimum": 64,
73
+ "default": 2048
74
+ },
75
+ "mcp": {
76
+ "title": "MCP server",
77
+ "type": "object",
78
+ "properties": {
79
+ "http": {
80
+ "title": "MCP over HTTP",
81
+ "type": "object",
82
+ "properties": {
83
+ "enabled": {
84
+ "title": "Serve MCP over HTTP",
85
+ "type": "boolean",
86
+ "default": false,
87
+ "description": "Lets Claude Desktop, Claude Code and Cursor control Homebridge through this plugin."
88
+ },
89
+ "host": {
90
+ "title": "Bind address",
91
+ "type": "string",
92
+ "default": "127.0.0.1",
93
+ "description": "127.0.0.1 keeps it on this machine. Use 0.0.0.0 only on a trusted network.",
94
+ "condition": { "functionBody": "return model.mcp && model.mcp.http && model.mcp.http.enabled;" }
95
+ },
96
+ "port": {
97
+ "title": "Port",
98
+ "type": "integer",
99
+ "default": 8582,
100
+ "minimum": 1,
101
+ "maximum": 65535,
102
+ "condition": { "functionBody": "return model.mcp && model.mcp.http && model.mcp.http.enabled;" }
103
+ },
104
+ "token": {
105
+ "title": "Client token",
106
+ "type": "string",
107
+ "x-sensitive": true,
108
+ "description": "MCP clients must send this as a bearer token.",
109
+ "condition": { "functionBody": "return model.mcp && model.mcp.http && model.mcp.http.enabled;" }
110
+ },
111
+ "homebridgeUrl": {
112
+ "title": "Homebridge UI URL",
113
+ "type": "string",
114
+ "format": "uri",
115
+ "placeholder": "http://127.0.0.1:8581",
116
+ "condition": { "functionBody": "return model.mcp && model.mcp.http && model.mcp.http.enabled;" }
117
+ },
118
+ "homebridgeToken": {
119
+ "title": "Homebridge API token",
120
+ "type": "string",
121
+ "x-sensitive": true,
122
+ "description": "An API token from Homebridge Glass UI (Settings, API tokens) the MCP tools act with. Use a read-only token to block changes.",
123
+ "condition": { "functionBody": "return model.mcp && model.mcp.http && model.mcp.http.enabled;" }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The agent loop: an LLM provider on one side, the Homebridge MCP tools on
3
+ * the other, connected in-process through the MCP SDK's in-memory transport.
4
+ */
5
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
6
+ import type { HomebridgeClient } from '../mcp/homebridge-client.js';
7
+ import type { AiProvider, ChatMessage, TokenUsage, ToolCall, ToolDefinition } from '@mp-consulting/homebridge-ai-core';
8
+ export type AgentEvent = {
9
+ type: 'text';
10
+ delta: string;
11
+ } | {
12
+ type: 'tool_call';
13
+ call: ToolCall;
14
+ } | {
15
+ type: 'tool_result';
16
+ call: ToolCall;
17
+ result: string;
18
+ isError: boolean;
19
+ } | {
20
+ type: 'step';
21
+ step: number;
22
+ };
23
+ export interface RunAgentOptions {
24
+ provider: AiProvider;
25
+ client: HomebridgeClient;
26
+ /** Expose only read-only tools. */
27
+ readOnly?: boolean;
28
+ system?: string;
29
+ messages: ChatMessage[];
30
+ /** Maximum model calls (default 8). */
31
+ maxSteps?: number;
32
+ maxOutputTokens?: number;
33
+ signal?: AbortSignal;
34
+ /** Progress: streamed text, tool calls and their results. Streaming is used when given. */
35
+ onEvent?: (event: AgentEvent) => void;
36
+ /**
37
+ * Asked before each destructive tool call (restart, config write, uninstall…).
38
+ * Without it, destructive calls are refused.
39
+ */
40
+ confirm?: (call: ToolCall) => Promise<boolean>;
41
+ }
42
+ export interface AgentToolCall extends ToolCall {
43
+ result: string;
44
+ isError: boolean;
45
+ }
46
+ export interface AgentResult {
47
+ text: string;
48
+ /** Model calls made. */
49
+ steps: number;
50
+ toolCalls: AgentToolCall[];
51
+ usage: TokenUsage;
52
+ /** The conversation including the agent's turns, to continue it later. */
53
+ messages: ChatMessage[];
54
+ /** True when the loop stopped at `maxSteps` with tool calls still pending. */
55
+ truncated: boolean;
56
+ }
57
+ /** MCP marks a tool destructive unless it says otherwise; read-only tools never are. */
58
+ export declare function isDestructive(tool: Tool): boolean;
59
+ export declare function toToolDefinition(tool: Tool): ToolDefinition;
60
+ export declare function runAgent(options: RunAgentOptions): Promise<AgentResult>;
61
+ //# sourceMappingURL=run-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-agent.d.ts","sourceRoot":"","sources":["../../src/agent/run-agent.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAE/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAA2B,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAEhJ,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,UAAU,CAAC;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAMjD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,cAAc,CAI3D;AAOD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAmE7E"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The agent loop: an LLM provider on one side, the Homebridge MCP tools on
3
+ * the other, connected in-process through the MCP SDK's in-memory transport.
4
+ */
5
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
6
+ import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
7
+ import { createServer, VERSION } from '../mcp/create-server.js';
8
+ import { PROMPTS, addUsage, complete } from '@mp-consulting/homebridge-ai-core';
9
+ /** MCP marks a tool destructive unless it says otherwise; read-only tools never are. */
10
+ export function isDestructive(tool) {
11
+ const a = tool.annotations;
12
+ if (a?.readOnlyHint === true) {
13
+ return false;
14
+ }
15
+ return a?.destructiveHint !== false;
16
+ }
17
+ export function toToolDefinition(tool) {
18
+ const inputSchema = { ...tool.inputSchema };
19
+ delete inputSchema.$schema;
20
+ return { name: tool.name, description: tool.description ?? tool.title ?? '', inputSchema };
21
+ }
22
+ function resultText(result) {
23
+ const content = (result.content ?? []);
24
+ return content.map((c) => (c.type === 'text' ? c.text : `[${c.type}]`)).join('\n');
25
+ }
26
+ export async function runAgent(options) {
27
+ const { provider, client, onEvent, signal } = options;
28
+ const maxSteps = options.maxSteps ?? 8;
29
+ const system = options.system ?? PROMPTS.ask.system;
30
+ const messages = [...options.messages];
31
+ const onText = onEvent ? (delta) => onEvent({ type: 'text', delta }) : undefined;
32
+ if (!provider.capabilities.tools) {
33
+ const result = await complete(provider, { system, messages, maxOutputTokens: options.maxOutputTokens, signal }, onText);
34
+ messages.push(result.message);
35
+ return { text: result.text, steps: 1, toolCalls: [], usage: result.usage, messages, truncated: false };
36
+ }
37
+ const server = createServer(client, { readOnly: options.readOnly, live: false });
38
+ const mcp = new Client({ name: 'homebridge-ai-kit-agent', version: VERSION });
39
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
40
+ try {
41
+ await Promise.all([server.connect(serverTransport), mcp.connect(clientTransport)]);
42
+ const { tools } = await mcp.listTools();
43
+ const destructive = new Set(tools.filter(isDestructive).map((t) => t.name));
44
+ const definitions = tools.map(toToolDefinition);
45
+ const toolCalls = [];
46
+ let usage = { inputTokens: 0, outputTokens: 0 };
47
+ let last;
48
+ let steps = 0;
49
+ while (steps < maxSteps) {
50
+ signal?.throwIfAborted();
51
+ steps++;
52
+ onEvent?.({ type: 'step', step: steps });
53
+ last = await complete(provider, { system, messages, tools: definitions, maxOutputTokens: options.maxOutputTokens, signal }, onText);
54
+ usage = addUsage(usage, last.usage);
55
+ messages.push(last.message);
56
+ if (last.toolCalls.length === 0) {
57
+ return { text: last.text, steps, toolCalls, usage, messages, truncated: false };
58
+ }
59
+ const results = [];
60
+ for (const call of last.toolCalls) {
61
+ onEvent?.({ type: 'tool_call', call });
62
+ let result;
63
+ let isError;
64
+ if (destructive.has(call.name) && !(await options.confirm?.(call))) {
65
+ result = `The user did not allow ${call.name}. Do not retry it; explain what it would have done instead.`;
66
+ isError = true;
67
+ }
68
+ else {
69
+ try {
70
+ const out = await mcp.callTool({ name: call.name, arguments: call.arguments });
71
+ result = resultText(out);
72
+ isError = out.isError === true;
73
+ }
74
+ catch (error) {
75
+ result = error instanceof Error ? error.message : String(error);
76
+ isError = true;
77
+ }
78
+ }
79
+ onEvent?.({ type: 'tool_result', call, result, isError });
80
+ toolCalls.push({ ...call, result, isError });
81
+ results.push({ type: 'tool_result', toolCallId: call.id, name: call.name, content: result, isError });
82
+ }
83
+ messages.push({ role: 'user', content: results });
84
+ }
85
+ return { text: last?.text ?? '', steps, toolCalls, usage, messages, truncated: true };
86
+ }
87
+ finally {
88
+ await mcp.close();
89
+ await server.close();
90
+ }
91
+ }
92
+ //# sourceMappingURL=run-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-agent.js","sourceRoot":"","sources":["../../src/agent/run-agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=homebridge-ai-kit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homebridge-ai-kit.d.ts","sourceRoot":"","sources":["../../src/bin/homebridge-ai-kit.ts"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { runHttpFromEnv, runStdioServer } from '../mcp/stdio.js';
4
+ const PROG = 'homebridge-ai-kit';
5
+ const USAGE = `Usage: ${PROG} mcp [--http [--port 8582] [--host 127.0.0.1]]
6
+
7
+ Commands:
8
+ mcp Run the Homebridge MCP server on stdio
9
+ mcp --http Serve MCP over Streamable HTTP instead (needs HOMEBRIDGE_AI_MCP_TOKEN)
10
+
11
+ Options:
12
+ --port <n> HTTP port (default 8582)
13
+ --host <addr> HTTP bind address (default 127.0.0.1)`;
14
+ let parsed;
15
+ function parse() {
16
+ return parseArgs({
17
+ allowPositionals: true,
18
+ options: {
19
+ http: { type: 'boolean' },
20
+ port: { type: 'string' },
21
+ host: { type: 'string' },
22
+ help: { type: 'boolean', short: 'h' },
23
+ },
24
+ });
25
+ }
26
+ try {
27
+ parsed = parse();
28
+ }
29
+ catch (error) {
30
+ console.error(`${PROG}: ${error.message}\n\n${USAGE}`);
31
+ process.exit(1);
32
+ }
33
+ const { values, positionals } = parsed;
34
+ const [command] = positionals;
35
+ if (values.help) {
36
+ console.error(USAGE);
37
+ process.exit(0);
38
+ }
39
+ switch (command) {
40
+ case 'mcp':
41
+ if (values.http) {
42
+ const port = values.port === undefined ? undefined : Number(values.port);
43
+ if (port !== undefined && (!Number.isInteger(port) || port <= 0 || port > 65535)) {
44
+ console.error(`${PROG}: --port must be a port number, got ${values.port}`);
45
+ process.exit(1);
46
+ }
47
+ await runHttpFromEnv(PROG, { port, host: values.host });
48
+ }
49
+ else {
50
+ await runStdioServer(PROG);
51
+ }
52
+ break;
53
+ case undefined:
54
+ console.error(USAGE);
55
+ process.exit(1);
56
+ break;
57
+ default:
58
+ console.error(`${PROG}: unknown command '${command}'\n\n${USAGE}`);
59
+ process.exit(1);
60
+ }
61
+ //# sourceMappingURL=homebridge-ai-kit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homebridge-ai-kit.js","sourceRoot":"","sources":["../../src/bin/homebridge-ai-kit.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjE,MAAM,IAAI,GAAG,mBAAmB,CAAC;AAEjC,MAAM,KAAK,GAAG,UAAU,IAAI;;;;;;;;uDAQ2B,CAAC;AAExD,IAAI,MAAgC,CAAC;AACrC,SAAS,KAAK;IACZ,OAAO,SAAS,CAAC;QACf,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE;YACP,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YACzB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACtC;KACF,CAAC,CAAC;AACL,CAAC;AAED,IAAI,CAAC;IACH,MAAM,GAAG,KAAK,EAAE,CAAC;AACnB,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,KAAM,KAAe,CAAC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC;IAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;AACvC,MAAM,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC;AAE9B,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;IAChB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,QAAQ,OAAO,EAAE,CAAC;IAChB,KAAK,KAAK;QACR,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACzE,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;gBACjF,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,uCAAuC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,MAAM,cAAc,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM;IACR,KAAK,SAAS;QACZ,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,MAAM;IACR;QACE,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,sBAAsB,OAAO,QAAQ,KAAK,EAAE,CAAC,CAAC;QACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=homebridge-mcp-server.d.ts.map