@coffeerunhobby/mcp-ha-connect 1.5.3 → 1.5.5

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/AGENTS.md ADDED
@@ -0,0 +1,411 @@
1
+ # mcp-ha-connect -- Codex Agent
2
+
3
+ ## Identity & Scope
4
+
5
+ This agent owns the **mcp-ha-connect MCP server** — a Model Context Protocol server for
6
+ Home Assistant, TP-Link Omada, and Local AI integration.
7
+
8
+ **Repo:** `github.com/coffeerunhobby/mcp-ha-connect`
9
+ **npm:** `@coffeerunhobby/mcp-ha-connect`
10
+ **Docker:** `ghcr.io/coffeerunhobby/mcp-ha-connect`
11
+ **Live endpoint:** Cloudflare domain → NAS Docker port 3000 (domain in NAS `.env`)
12
+
13
+ **Working directory:** `C:\workspace\mcp` — this is the canonical copy.
14
+ `W:\mcp-homeassistant` is a stale export at v0.8.0 — ignore it.
15
+
16
+ ---
17
+
18
+ ## Stack
19
+
20
+ - **Runtime:** Node.js 20+ (dev machine uses nvm)
21
+ - **Language:** TypeScript, compiled to `dist/` via `tsc`
22
+ - **MCP SDK:** `@modelcontextprotocol/sdk`
23
+ - **Transport:** Streamable HTTP (port 3000, path `/mcp`)
24
+ - **Auth:** JWT bearer tokens with RBAC permission system
25
+ - **Plugins:** Home Assistant · TP-Link Omada SDN · Local AI (Ollama/OpenAI-compat)
26
+ - **Tests:** Vitest (829 unit tests) + integration tests in `tests/integration/`
27
+ - **Container:** Alpine Node image, deployed on Synology NAS via Docker Compose
28
+
29
+ ---
30
+
31
+ ## Infrastructure
32
+
33
+ | Resource | Address | Notes |
34
+ |---|---|---|
35
+ | **MCP server** | `mcpserver.10.0.0.18.nip.io:3000` | NAS Docker container |
36
+ | **Home Assistant** | `homeassistant.10.0.0.19.nip.io:8123` | |
37
+ | **Omada controller** | `omada.10.0.0.18.nip.io:8043` | HTTPS, self-signed cert |
38
+ | **Ollama** | `ollama.10.0.0.17.nip.io:11434` | model `phi4:14b` |
39
+ | **n8n** | `n8n.10.0.0.18.nip.io:5678` | also exposed via Cloudflare |
40
+ | **NAS Docker path** | `/volume1/docker/mcp-ha-connect/` | compose.yaml + .env |
41
+ | **Cloudflare** | proxies public domain → NAS port 3000 | domain stored in `.env` |
42
+
43
+ ---
44
+
45
+ ## Secrets & Credentials
46
+
47
+ All secrets live in the NAS `.env` file and as **Windows User environment variables**.
48
+ Never store token values in source files, commit messages, or this file.
49
+
50
+ | Variable | Purpose |
51
+ |---|---|
52
+ | `NPM_MCP_HA_TOKEN` | npm Granular token for publishing |
53
+ | `GHCR_TOKEN` | GitHub PAT with `write:packages` scope |
54
+
55
+ ### Retrieve for use
56
+
57
+ ```powershell
58
+ $npmToken = [Environment]::GetEnvironmentVariable('NPM_MCP_HA_TOKEN', 'User')
59
+ $ghcrToken = [Environment]::GetEnvironmentVariable('GHCR_TOKEN', 'User')
60
+ ```
61
+
62
+ ### npm token requirements
63
+
64
+ The npm token must be **Granular type** with:
65
+ - Scope `@coffeerunhobby` → **Read and write** (scope-level, not per-package)
66
+ - **Bypass two-factor authentication** ✅ checked
67
+
68
+ Create at: npmjs.com → Access Tokens → Generate New Token → Granular Access Token
69
+
70
+ **Critical:** Never run `npm logout` — it revokes the token server-side and deletes it
71
+ from npmjs.com. If the token disappears from the web UI, that's why.
72
+
73
+ ---
74
+
75
+ ## Release Process
76
+
77
+ ### 1. Pre-flight
78
+
79
+ ```powershell
80
+ cd C:\workspace\mcp
81
+ npm test # all tests must pass
82
+ npm run build # must compile clean
83
+ npm run lint # ZERO errors AND zero warnings (see Code Quality policy)
84
+ ```
85
+
86
+ ### 2. Version bump
87
+
88
+ Update **both** files to the new version:
89
+ - `package.json` → `"version": "X.Y.Z"`
90
+ - `src/version.ts` → `export const VERSION = 'X.Y.Z';`
91
+
92
+ Update `docs/CHANGELOG.md` with the new version section.
93
+
94
+ ### 3. Commit & tag
95
+
96
+ ```powershell
97
+ git add .
98
+ git commit -m "vX.Y.Z - <short description>"
99
+ git tag vX.Y.Z
100
+ git push origin main
101
+ git push origin vX.Y.Z
102
+ ```
103
+
104
+ Create the GitHub Release at:
105
+ `https://github.com/coffeerunhobby/mcp-ha-connect/releases/new?tag=vX.Y.Z`
106
+
107
+ ### 4. npm publish
108
+
109
+ **Preferred: interactive login (browser).** Granular tokens keep getting revoked, so the
110
+ reliable path is an interactive `npm login`, which opens the browser for auth. The agent
111
+ **cannot** complete this alone — it MUST ask the user to run/confirm the browser login,
112
+ then publish together:
113
+
114
+ ```powershell
115
+ cd C:\workspace\mcp
116
+ npm login # opens browser — USER completes this, agent waits
117
+ npm whoami # must print the account name, not 403
118
+ npm publish --access public
119
+ ```
120
+
121
+ **Do NOT run `npm logout`** afterward — it revokes any granular token server-side.
122
+
123
+ > Agent rule: when a release reaches this step, STOP and hand off to the user — the
124
+ > **user owns npm publish** and runs it themselves. They authenticate with a **passkey**
125
+ > (WebAuthn), so the publish cannot be automated from the agent side and there is no
126
+ > usable stored/granular token to fall back on. The agent's job at this step is:
127
+ > 1. Get everything else release-ready (tag, GitHub Release, all gates green).
128
+ > 2. Tell the user the package is ready to publish and let them run
129
+ > `npm publish --access public` (passkey prompt).
130
+ > 3. Verify afterward with `npm view @coffeerunhobby/mcp-ha-connect version` /
131
+ > `dist-tags` — do NOT rely on `npm whoami` from the agent (it returns 401 because the
132
+ > agent has no session; that is expected, not a failure).
133
+ > Do not attempt `npm login`/`npm publish` from the agent, and never fall back to a stored
134
+ > token — confirmed (2026-06-09, v1.5.1) that the user handles this leg via passkey.
135
+
136
+ ### 5. Docker build & push
137
+
138
+ Docker Desktop must be running.
139
+
140
+ ```powershell
141
+ $ghcrToken = [Environment]::GetEnvironmentVariable('GHCR_TOKEN', 'User')
142
+ $ghcrToken | docker login ghcr.io -u coffeerunhobby --password-stdin
143
+
144
+ cd C:\workspace\mcp
145
+ docker build --no-cache `
146
+ -t ghcr.io/coffeerunhobby/mcp-ha-connect:X.Y.Z `
147
+ -t ghcr.io/coffeerunhobby/mcp-ha-connect:latest .
148
+
149
+ docker push ghcr.io/coffeerunhobby/mcp-ha-connect:X.Y.Z
150
+ docker push ghcr.io/coffeerunhobby/mcp-ha-connect:latest
151
+ ```
152
+
153
+ Use `docker build`, not `docker buildx build --platform`. The NAS is amd64 and a plain
154
+ build on a Windows/amd64 host produces the correct image.
155
+
156
+ ### 6. Deploy to NAS
157
+
158
+ ```bash
159
+ cd /volume1/docker/mcp-ha-connect
160
+ docker compose pull && docker compose up -d
161
+ docker compose logs --tail=30
162
+ ```
163
+
164
+ ---
165
+
166
+ ## NAS `.env` — critical settings
167
+
168
+ ```env
169
+ MCP_HTTP_BIND_ADDR=0.0.0.0
170
+ ```
171
+
172
+ Must be `0.0.0.0`, not `127.0.0.1`. Docker port mapping requires the server to listen on
173
+ all interfaces inside the container — loopback only means Cloudflare gets a 502.
174
+
175
+ ```env
176
+ MCP_HTTP_ALLOWED_ORIGINS=...,<cloudflare-domain>,https://<cloudflare-domain>
177
+ ```
178
+
179
+ Must include both the bare hostname AND the `https://` form of the Cloudflare domain.
180
+ The MCP SDK checks the `host` header (no protocol) for DNS rebinding protection AND
181
+ the `origin` header (with protocol) for CORS.
182
+
183
+ ---
184
+
185
+ ## Omada SDN — known issues
186
+
187
+ **After upgrading the Omada controller version**, the `OMADA_OMADAC_ID` changes.
188
+ Error code `-44106` is returned for BOTH wrong credentials AND wrong `OMADA_OMADAC_ID` —
189
+ the API does not distinguish between them.
190
+
191
+ **To get the new Omada ID:**
192
+ Omada web UI → Settings → Open API → click the eye icon → copy **Omada ID**.
193
+
194
+ ```bash
195
+ sed -i 's/OMADA_OMADAC_ID=.*/OMADA_OMADAC_ID=<new-id>/' /volume1/docker/mcp-ha-connect/.env
196
+ docker compose up -d
197
+ ```
198
+
199
+ If Omada is unreachable or credentials are wrong, the server **gracefully degrades** —
200
+ Omada tools are disabled but HA and AI keep working. Set `OMADA_PLUGIN_ENABLED=false`
201
+ to disable Omada entirely while troubleshooting.
202
+
203
+ **`OMADA_SITE_ID` is validated at startup against the live site list** (the controller is
204
+ the source of truth; the env var is only a selector). If `OMADA_SITE_ID` is set but **not**
205
+ present on the controller — a site id can drift after a controller migration, exactly like
206
+ `OMADA_OMADAC_ID` — the **Omada plugin is disabled** at boot with a `logger.error` that
207
+ names the bad value and lists the valid sites (HA + AI stay up). This turns the old failure
208
+ mode (a silent default scoping every read to a dead site → later "user does not have
209
+ permissions to access this site") into a loud, self-diagnosing startup error. Fix by setting
210
+ `OMADA_SITE_ID` to one of the listed ids. Validation lives in `checkConfiguredSite()`
211
+ (`src/omadaClient/site.ts`); a *valid* default still auto-resolves, so site-scoped reads need
212
+ no explicit `siteId`.
213
+
214
+ ---
215
+
216
+ ## Code Quality — fix every error and warning
217
+
218
+ **Always fix errors AND warnings. Never label something "pre-existing debt" and move on.**
219
+
220
+ - `npm run lint`, `npm run build`, and `npm test` must all be **completely clean** —
221
+ zero errors, zero warnings — before a change is considered done.
222
+ - If you touch a file (or a release surfaces lint output) and find existing errors or
223
+ warnings, they are now yours to fix. "It was already broken" is not an acceptable
224
+ reason to leave it.
225
+ - **Exception — release timing:** do not cut a patch release *solely* to clean up lint.
226
+ If quality issues are found right after a release has shipped, fold the fixes into the
227
+ **next feature's** commit rather than churning a `vX.Y.(Z+1)`. Outstanding cleanup of
228
+ this kind is tracked in `.Codex/NEXT-SESSION.md` so it isn't forgotten.
229
+
230
+ > No lint cleanup currently outstanding. (The former `Function`-type warnings in the
231
+ > test mock helpers were resolved — `npm run lint --max-warnings=0` is clean as of v1.5.0.)
232
+
233
+ ---
234
+
235
+ ## Development
236
+
237
+ ```powershell
238
+ cd C:\workspace\mcp
239
+ npm run dev # tsx watch with .env
240
+ npm test # vitest (excludes integration)
241
+ npm run test:watch # watch mode
242
+ npm run lint # eslint
243
+ npm run build # tsc compile to dist/
244
+ npm run generate:jwt # generate a test JWT token
245
+ ```
246
+
247
+ ### Integration tests (requires live .env)
248
+
249
+ ```powershell
250
+ npm run test:integration
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Live testing against the running server
256
+
257
+ The dev machine can exercise the deployed MCP server two ways:
258
+
259
+ 1. **Connected `homeassistant` MCP in Codex Desktop** — this agent has the live server
260
+ wired in as an MCP connection, so HA/Omada tools can be invoked directly (no curl, no
261
+ manual token; the configured bearer is used). **Caveat:** the MCP client caches the tool
262
+ list at *connection time*. After flipping `MCP_TOOL_REGISTRATION_MODE` (eager ⇄ graph)
263
+ and redeploying, that cached list is **stale** — Codex Desktop must reconnect (restart)
264
+ before the new tool surface (`omada_browse`/`omada_read`, or the typed getters) shows up.
265
+ Until it reconnects, calling a tool the live server no longer registers will fail.
266
+
267
+ 2. **Direct `/mcp` curl** — generate a token with
268
+ `npm run generate:jwt -- --sub <user> --exp 1h` (signs with the local `.env`
269
+ `MCP_AUTH_SECRET`, which currently matches the NAS), then POST JSON-RPC to the server's
270
+ `/mcp` with headers `Authorization: Bearer <tok>`, `Content-Type: application/json`,
271
+ `Accept: application/json, text/event-stream`. Responses come back as SSE
272
+ (`data: {...}` lines). `tools/list` and `tools/call` work **without** an initialize
273
+ handshake in stateless mode. The reachable server address lives in
274
+ `.Codex/NEXT-SESSION.md` (gitignored — never put the real IP/domain here).
275
+
276
+ Permissions are resolved **server-side** from the token's `sub` via `MCP_PERMISSIONS_CONFIG`
277
+ (they are NOT carried in the token). Pick a `sub` whose mapped role has the bit you need —
278
+ e.g. the ADMIN-gated `/security/threats` node needs a `sub` mapped to an ADMIN role. The NAS
279
+ role map differs from the local `.env`, so don't assume `sub=admin` is actually ADMIN there.
280
+
281
+ ---
282
+
283
+ ## Never assume. Always verify.
284
+
285
+ - **Cloudflare 502**: check the container is running (`docker compose ps`) and
286
+ `MCP_HTTP_BIND_ADDR=0.0.0.0` is set.
287
+ - **MCP client connects but tools fail**: check `MCP_HTTP_ALLOWED_ORIGINS` includes
288
+ the Cloudflare domain in both bare and `https://` form.
289
+ - **Codex Desktop shows "Server disconnected" but `/health` is 200**: the client's
290
+ mcp-remote auth header is malformed. A `"Authorization: Bearer ${MCP_AUTH_TOKEN}"`
291
+ placeholder does NOT expand on Windows (`cmd /c` uses `%VAR%`, mcp-remote doesn't
292
+ substitute `${VAR}`) → literal text is sent → server 401 → mcp-remote drops into a
293
+ failing OAuth flow. Fix: inline the literal JWT in `claude_desktop_config.json`'s
294
+ `--header` arg, and add `-y` to `npx` so first run doesn't hang. Verify the server
295
+ side first with a direct `curl` POST to `/mcp` carrying the token.
296
+ - **npm publish returns 404 on PUT**: token likely has per-package scope instead of
297
+ scope-level access, or "Bypass 2FA" is not ticked. Delete token and recreate.
298
+ - **Omada auth error -44106**: check `OMADA_OMADAC_ID` first, not just credentials —
299
+ this error code covers both cases.
300
+ - **Before a release**: always run `npm test` and `npm run build` clean.
301
+ - **On release, bump the version everywhere**: `package.json`, `src/version.ts`,
302
+ `docs/CHANGELOG.md`, AND `openapi.json` (`info.version`) — the last one drifts easily.
303
+
304
+ ---
305
+
306
+ ## Tool registration modes (`MCP_TOOL_REGISTRATION_MODE`)
307
+
308
+ Implemented as a **resource-graph** mode for the Omada plugin (config parses the var;
309
+ `config.toolRegistrationMode` threads through `createServer` → `registerAllTools` →
310
+ `registerOmadaTools`). Values:
311
+
312
+ - **`eager`** (default) — register every typed tool (~26 Omada tools). Today's behavior;
313
+ fully backwards-compatible.
314
+ - **`graph`** — register only `omada_browse` + `omada_read` + the 5 typed write/action
315
+ tools. Reads collapse into a discoverable resource graph (`src/tools/omada/namespace.ts`
316
+ manifest + `src/tools/omada/graph.ts`), shrinking the tool-schema budget for low-context
317
+ models while still exposing every read endpoint.
318
+
319
+ Design invariants (don't regress these):
320
+ - **browse returns TYPES, not instances** — `omada_browse(path)` lists permission-filtered
321
+ child resource types + metadata (kind, permission, pagination, params). It never
322
+ enumerates MACs or calls the controller.
323
+ - **read is per-path RBAC, fail-closed** — `omada_read` declares NO static tool permission;
324
+ each manifest node declares its own bit (most reads = QUERY; the `/security/*` subtree =
325
+ ADMIN). Unknown / container / under-privileged / missing-required-param paths all return a
326
+ clean error and never fetch.
327
+ - **single-page pagination** — paginated nodes forward one page/pageSize to a single GET via
328
+ `OmadaClient.readResource()`; they never walk every page.
329
+ - **graph is a complete superset of eager reads** — every eager read getter has a graph node,
330
+ including the ADMIN-gated IDS/IPS threat-management log at `/security/threats`. Its mandatory
331
+ epoch time-window is enforced via required `params.startTime`/`params.endTime` (epoch seconds),
332
+ validated by `omada_read` before any controller call.
333
+
334
+ HA and AI plugins are unaffected (still eager). Writes stay typed + individually gated in
335
+ both modes — generic write verbs were deliberately rejected (prompt-injection / hallucination
336
+ surface).
337
+
338
+ ---
339
+
340
+ ## Security
341
+
342
+ This server controls real devices (HA, Omada) and is exposed publicly via Cloudflare, so
343
+ treat it as security-sensitive. A full audit (2026-06-07) with file/line findings is in
344
+ `.Codex/NEXT-SESSION.md`; this section is the **durable policy** distilled from it.
345
+
346
+ ### Dependencies — update, don't rewrite
347
+
348
+ - Runtime deps are intentionally tiny: `@modelcontextprotocol/sdk`, `ws`, `zod`.
349
+ **Do not hand-roll replacements** for these (or for transitive libs like `hono`,
350
+ `ajv`, `path-to-regexp`). A homegrown version loses community review and adds more
351
+ bugs than it removes. Zod and the MCP SDK earn their keep.
352
+ - The alarming `npm audit` list (`hono`, `@hono/node-server`, `fast-uri`, `ajv`,
353
+ `path-to-regexp`, `qs`) is **entirely transitive under the SDK**:
354
+ `@modelcontextprotocol/sdk → @hono/node-server → hono`. **Bumping the SDK fixes most
355
+ of them.** `ws` is the one direct dep to bump on its own.
356
+ - Triage with `npm audit --omit=dev` — dev-only vulns (vitest/esbuild/tsx) don't ship.
357
+ The question is *reachability in production*, not the raw count.
358
+ - Remediate on a branch: `npm install @modelcontextprotocol/sdk@latest ws@latest`, then
359
+ `npm test && npm run build && npm audit --omit=dev`. Commit `package-lock.json`;
360
+ CI/Docker use `npm ci`, not `npm install`. Widen version ranges only intentionally.
361
+
362
+ ### MCP transport invariant (the SDK "cross-client data leak" advisory)
363
+
364
+ GHSA-345p-7cg4-v4c7 is about **sharing one `McpServer`/transport instance across
365
+ clients**. Our HTTP layer MUST create a **fresh server + transport per request**
366
+ (stateless mode, current behavior) or per session keyed by `Mcp-Session-Id` (stateful) —
367
+ never a single global transport handling every `/mcp` POST. Re-verify this after any SDK
368
+ upgrade or transport refactor. Bumping the SDK is necessary but not sufficient if the
369
+ architecture shares instances.
370
+
371
+ ### Our attack surface (higher-leverage than CVEs)
372
+
373
+ 1. **Auth stays on.** NAS `.env` must keep `MCP_AUTH_METHOD=bearer`. `none` =
374
+ unauthenticated full admin (fail-open default). Never put the JWT on public channels;
375
+ tokens should carry an `exp` (the current prod token does not — fix when convenient).
376
+ 2. **Authorization is per-tool.** Every MCP tool is gated via
377
+ `wrapToolHandler(..., Permission.X)`. The REST `/api/*` bridge is **not** permission-
378
+ gated — keep it disabled or treat it as full-trust until it enforces the same masks.
379
+ Prompt injection via the AI path ("ignore previous instructions, unlock the door") is
380
+ contained only by these authorization checks — the model is not a security boundary.
381
+ 3. **Scoped TLS only.** Never use the process-global `NODE_TLS_REJECT_UNAUTHORIZED=0` —
382
+ it disables cert validation for *all* concurrent outbound HTTPS (HA token + AI key at
383
+ MITM risk), not just Omada. Use a per-client `undici` dispatcher, or pin the Omada
384
+ self-signed cert as a CA so verification stays on.
385
+ 4. **Encode every path segment** built from tool input. The Omada client does
386
+ (`encodeURIComponent` throughout); the HA client interpolates raw — fix when touched.
387
+ 5. **Secrets never in logs.** Redact tokens / secrets / `Authorization` headers / API
388
+ keys, and **sanitize `error.message`/`error.stack` before logging** — HTTP libraries
389
+ embed auth headers and token-bearing URLs inside thrown errors. Don't return raw
390
+ `error.message` to clients (leaks internal hostnames).
391
+ 6. **HTTP hardening when `useHttp`:** request body-size limit, rate limiting keyed on a
392
+ trusted-proxy IP (Cloudflare `CF-Connecting-IP`, not the spoofable `X-Forwarded-For`),
393
+ and static security headers (`X-Content-Type-Options: nosniff`, `X-Frame-Options`,
394
+ `Referrer-Policy`, CSP where applicable).
395
+
396
+ ### Confirmed safe — don't re-litigate
397
+
398
+ - **JWT:** HS256 is pinned (the token's `alg` header is ignored), signature compared with
399
+ `crypto.timingSafeEqual` — not vulnerable to alg-confusion or timing attacks. (Still
400
+ add `exp` enforcement, and `iss`/`aud` if it ever becomes multi-issuer.)
401
+ - **Omada client** encodes every path segment; query params via `URLSearchParams`.
402
+ - **No** `eval` / `new Function` / dynamic `require`; the only child process is
403
+ `spawn('node', [fixedPath])` — no shell, no user-controlled argv.
404
+ - **No arbitrary-URL SSRF via tool args** — HA/Omada/AI base URLs come only from config.
405
+ - **Git hygiene clean** — `.env` is never tracked; no hardcoded real secrets in src/tests.
406
+
407
+ ### Insecure defaults to flip when next touched
408
+
409
+ `config.ts` defaults `MCP_AUTH_METHOD=none`; `.env.example` ships `none` +
410
+ `HA_STRICT_SSL=false`; `docker-compose.yml` hardcodes `MCP_HTTP_ALLOWED_ORIGINS=*`.
411
+ None of these reflect the (secure) live deployment — make the secure values the defaults.
package/Dockerfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # syntax=docker/dockerfile:1.7
2
2
 
3
- FROM node:25-alpine AS deps
3
+ FROM node:26-alpine AS deps
4
4
  WORKDIR /app
5
5
  COPY package*.json ./
6
6
  # npm ci installs exactly from the committed package-lock.json (reproducible) and
@@ -8,13 +8,13 @@ COPY package*.json ./
8
8
  # above pulls in package-lock.json, so the lockfile is present in this stage.
9
9
  RUN npm ci
10
10
 
11
- FROM node:25-alpine AS build
11
+ FROM node:26-alpine AS build
12
12
  WORKDIR /app
13
13
  COPY --from=deps /app/node_modules ./node_modules
14
14
  COPY . .
15
15
  RUN npm run build
16
16
 
17
- FROM node:25-alpine AS runtime
17
+ FROM node:26-alpine AS runtime
18
18
 
19
19
  # OCI metadata labels
20
20
  LABEL org.opencontainers.image.title="MCP-HA-Connect"
@@ -27,7 +27,9 @@ LABEL org.opencontainers.image.licenses="MIT"
27
27
 
28
28
  WORKDIR /app
29
29
  ENV NODE_ENV=production
30
- RUN apk add --no-cache curl
30
+ # apk upgrade: pick up Alpine security patches even when the node base tag lags
31
+ # (the 2026-07 Trivy scan's 24 libssl/libcrypto CVEs were exactly this rot).
32
+ RUN apk upgrade --no-cache && apk add --no-cache curl
31
33
  COPY package*.json ./
32
34
  # Reproducible prod-only install from the lockfile (drops devDependencies).
33
35
  RUN npm ci --omit=dev
@@ -37,4 +39,6 @@ COPY --from=build /app/dist ./dist
37
39
  # Built artifacts under /app are world-readable, so no chown is required; the
38
40
  # server listens on 3000 (>1024) which needs no privileged capability.
39
41
  USER node
40
- CMD ["node", "--experimental-quic", "dist/index.js"]
42
+ # --experimental-quic was dropped 2026-07-10: nothing in src/ uses QUIC, and the
43
+ # flag tied the image to a specific Node major (25, now EOL) for no benefit.
44
+ CMD ["node", "dist/index.js"]
@@ -75,7 +75,7 @@ export class NetworkOperations {
75
75
  */
76
76
  async getSsidList(wlanId, siteId) {
77
77
  if (!wlanId) {
78
- throw new Error('A wlanId must be provided. Use getWlanGroupList to get available WLAN group IDs.');
78
+ throw new Error('A wlanId must be provided. Use omada_read at path /wifi/groups to get available WLAN group IDs.');
79
79
  }
80
80
  const resolvedSiteId = this.site.resolveSiteId(siteId);
81
81
  const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/wireless-network/wlans/${encodeURIComponent(wlanId)}/ssids`);
@@ -90,10 +90,10 @@ export class NetworkOperations {
90
90
  */
91
91
  async getSsidDetail(wlanId, ssidId, siteId) {
92
92
  if (!wlanId) {
93
- throw new Error('A wlanId must be provided. Use getWlanGroupList to get available WLAN group IDs.');
93
+ throw new Error('A wlanId must be provided. Use omada_read at path /wifi/groups to get available WLAN group IDs.');
94
94
  }
95
95
  if (!ssidId) {
96
- throw new Error('An ssidId must be provided. Use getSsidList to get available SSID IDs.');
96
+ throw new Error('An ssidId must be provided. Use omada_read at path /wifi/ssids to get available SSID IDs.');
97
97
  }
98
98
  const resolvedSiteId = this.site.resolveSiteId(siteId);
99
99
  const path = this.buildPath(`/sites/${encodeURIComponent(resolvedSiteId)}/wireless-network/wlans/${encodeURIComponent(wlanId)}/ssids/${encodeURIComponent(ssidId)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/omadaClient/network.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/omadaClient/network.ts"],"names":[],"mappings":"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"}
@@ -52,7 +52,7 @@ export class SiteOperations {
52
52
  if (this.defaultSiteId) {
53
53
  return this.defaultSiteId;
54
54
  }
55
- throw new Error('A site id must be provided either in the environment or as a parameter.');
55
+ throw new Error('A site id must be provided either in the environment or as a parameter. Use omada_browse at path / to discover available sites and their IDs.');
56
56
  }
57
57
  }
58
58
  //# sourceMappingURL=site.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"site.js","sourceRoot":"","sources":["../../src/omadaClient/site.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,mBAAmB,CAC/B,gBAAoC,EACpC,KAAyB;IAEzB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,gBAAgB,CAAC,EAAE,CAAC;QACnD,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,gCAAgC,CAAC;IAC7G,OAAO,kBAAkB,gBAAgB,mEAAmE,SAAS,GAAG,CAAC;AAC7H,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,cAAc;IAEF;IACA;IACA;IAHrB,YACqB,OAAuB,EACvB,SAAmC,EACnC,aAAsB;QAFtB,YAAO,GAAP,OAAO,CAAgB;QACvB,cAAS,GAAT,SAAS,CAA0B;QACnC,kBAAa,GAAb,aAAa,CAAS;IACxC,CAAC;IAEJ;;OAEG;IACI,KAAK,CAAC,SAAS;QAClB,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,CAAmB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzF,CAAC;IAED;;;OAGG;IACI,aAAa,CAAC,MAAe;QAChC,IAAI,MAAM,EAAE,CAAC;YACT,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,aAAa,CAAC;QAC9B,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;IAC/F,CAAC;CACJ"}
1
+ {"version":3,"file":"site.js","sourceRoot":"","sources":["../../src/omadaClient/site.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,mBAAmB,CAC/B,gBAAoC,EACpC,KAAyB;IAEzB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,gBAAgB,CAAC,EAAE,CAAC;QACnD,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,gCAAgC,CAAC;IAC7G,OAAO,kBAAkB,gBAAgB,mEAAmE,SAAS,GAAG,CAAC;AAC7H,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,cAAc;IAEF;IACA;IACA;IAHrB,YACqB,OAAuB,EACvB,SAAmC,EACnC,aAAsB;QAFtB,YAAO,GAAP,OAAO,CAAgB;QACvB,cAAS,GAAT,SAAS,CAA0B;QACnC,kBAAa,GAAb,aAAa,CAAS;IACxC,CAAC;IAEJ;;OAEG;IACI,KAAK,CAAC,SAAS;QAClB,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,CAAmB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzF,CAAC;IAED;;;OAGG;IACI,aAAa,CAAC,MAAe;QAChC,IAAI,MAAM,EAAE,CAAC;YACT,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,aAAa,CAAC;QAC9B,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,+IAA+I,CAAC,CAAC;IACrK,CAAC;CACJ"}
@@ -55,7 +55,7 @@ export function registerAllResources(server, client) {
55
55
  {
56
56
  uri: uri.href,
57
57
  mimeType: 'application/json',
58
- text: JSON.stringify({ error: 'Entity not found', entity_id: entityId }),
58
+ text: JSON.stringify({ error: 'Entity not found', entity_id: entityId, hint: 'Use the searchEntities tool with a keyword to find the correct entity_id' }),
59
59
  },
60
60
  ],
61
61
  };
@@ -88,7 +88,7 @@ export function registerAllResources(server, client) {
88
88
  {
89
89
  uri: uri.href,
90
90
  mimeType: 'application/json',
91
- text: JSON.stringify({ error: 'Entity not found', entity_id: entityId }),
91
+ text: JSON.stringify({ error: 'Entity not found', entity_id: entityId, hint: 'Use the searchEntities tool with a keyword to find the correct entity_id' }),
92
92
  },
93
93
  ],
94
94
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ export function registerEntityActionTool(server, client) {
9
9
  }, wrapToolHandler('entityAction', async ({ entity_id, action }) => {
10
10
  const domain = entity_id.split('.')[0];
11
11
  if (!domain) {
12
- return toToolResult({ error: 'Invalid entity_id format' }, true);
12
+ return toToolResult({ error: 'Invalid entity_id format', hint: 'entity_id must be in domain.name format such as light.living_room — use the searchEntities tool to discover valid entity IDs' }, true);
13
13
  }
14
14
  const result = await client.callService({
15
15
  domain,
@@ -1 +1 @@
1
- {"version":3,"file":"entityAction.js","sourceRoot":"","sources":["../../../src/tools/homeassistant/entityAction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK7F,MAAM,UAAU,wBAAwB,CAAC,MAAiB,EAAE,MAAgB;IAC1E,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,WAAW,EAAE,qGAAqG;QAClH,WAAW,EAAE,kBAAkB,CAAC,KAAK;KACtC,EACD,eAAe,CAAC,cAAc,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAoB,EAAE,EAAE;QAChF,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,YAAY,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,EAAE,IAAI,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;YACtC,MAAM;YACN,OAAO,EAAE,MAAM;YACf,MAAM,EAAE,EAAE,SAAS,EAAE;SACtB,CAAC,CAAC;QACH,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACrF,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CACvB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"entityAction.js","sourceRoot":"","sources":["../../../src/tools/homeassistant/entityAction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK7F,MAAM,UAAU,wBAAwB,CAAC,MAAiB,EAAE,MAAgB;IAC1E,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,WAAW,EAAE,qGAAqG;QAClH,WAAW,EAAE,kBAAkB,CAAC,KAAK;KACtC,EACD,eAAe,CAAC,cAAc,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAoB,EAAE,EAAE;QAChF,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,YAAY,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,IAAI,EAAE,8HAA8H,EAAE,EAAE,IAAI,CAAC,CAAC;QACzM,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;YACtC,MAAM;YACN,OAAO,EAAE,MAAM;YACf,MAAM,EAAE,EAAE,SAAS,EAAE;SACtB,CAAC,CAAC;QACH,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACrF,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CACvB,CAAC;AACJ,CAAC"}
@@ -9,7 +9,7 @@ export function registerGetStateTool(server, client) {
9
9
  }, wrapToolHandler('getState', async ({ entity_id }) => {
10
10
  const state = await client.getState(entity_id);
11
11
  if (!state) {
12
- return toToolResult({ error: 'Entity not found', entity_id }, true);
12
+ return toToolResult({ error: 'Entity not found', entity_id, hint: 'Use the searchEntities tool with a keyword to find the correct entity_id' }, true);
13
13
  }
14
14
  return toToolResult(state);
15
15
  }, Permission.QUERY));
@@ -1 +1 @@
1
- {"version":3,"file":"getState.js","sourceRoot":"","sources":["../../../src/tools/homeassistant/getState.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEzF,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAgB;IACtE,MAAM,CAAC,YAAY,CACjB,UAAU,EACV;QACE,WAAW,EAAE,kDAAkD;QAC/D,WAAW,EAAE,cAAc,CAAC,KAAK;KAClC,EACD,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,EAAE;QACzE,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,EAAE,IAAI,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CACrB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"getState.js","sourceRoot":"","sources":["../../../src/tools/homeassistant/getState.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEzF,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAgB;IACtE,MAAM,CAAC,YAAY,CACjB,UAAU,EACV;QACE,WAAW,EAAE,kDAAkD;QAC/D,WAAW,EAAE,cAAc,CAAC,KAAK;KAClC,EACD,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,EAAE;QACzE,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,IAAI,EAAE,0EAA0E,EAAE,EAAE,IAAI,CAAC,CAAC;QACxJ,CAAC;QACD,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CACrB,CAAC;AACJ,CAAC"}
package/dist/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * Package version - hardcoded at build time for reliability
3
3
  * Keep in sync with package.json version
4
4
  */
5
- export declare const VERSION = "1.5.3";
5
+ export declare const VERSION = "1.5.5";
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  * Package version - hardcoded at build time for reliability
3
3
  * Keep in sync with package.json version
4
4
  */
5
- export const VERSION = '1.5.3';
5
+ export const VERSION = '1.5.5';
6
6
  //# sourceMappingURL=version.js.map
package/docs/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ### 1.5.4
2
+ **Improvement — actionable error hints so agents self-correct instead of stalling.**
3
+ Not-found / invalid-input errors now carry a next-step pointer toward the tool that
4
+ resolves them, cutting the retry loops and wrong guesses that waste round-trips:
5
+ - `getState`, `entityAction`, and the `hass://entities/{id}` resources now return a
6
+ `hint` field on a missing/malformed `entity_id`, pointing at `searchEntities` (and the
7
+ required `domain.name` format).
8
+ - Omada `resolveSiteId` with no site configured now hints to `omada_browse` at `/` to
9
+ discover available sites and their IDs.
10
+ - Omada SSID getters with a missing `wlanId`/`ssidId` had stale tool references
11
+ (`getWlanGroupList`/`getSsidList`) corrected to the real graph-mode paths
12
+ (`omada_read /wifi/groups`, `omada_read /wifi/ssids`).
13
+
14
+ Purely additive — no API, schema, or behavior changes; only the human/agent-readable
15
+ error text gains guidance.
16
+
1
17
  ### 1.5.3
2
18
  **Fix — allow the `x-session-id` request header in CORS so Open WebUI tool calls work.**
3
19
  Open WebUI sends an `x-session-id` header on every OpenAPI tool call. The REST bridge's
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Home Assistant MCP Wrapper",
5
- "version": "1.5.3",
5
+ "version": "1.5.5",
6
6
  "description": "REST endpoints that wrap MCP tools for Home Assistant integration. Raw JSON-RPC passthrough available at POST / for n8n compatibility."
7
7
  },
8
8
  "servers": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffeerunhobby/mcp-ha-connect",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "Smart home MCP server for Home Assistant plus Local AI integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -85,7 +85,7 @@ export class NetworkOperations {
85
85
  */
86
86
  public async getSsidList(wlanId: string, siteId?: string): Promise<unknown[]> {
87
87
  if (!wlanId) {
88
- throw new Error('A wlanId must be provided. Use getWlanGroupList to get available WLAN group IDs.');
88
+ throw new Error('A wlanId must be provided. Use omada_read at path /wifi/groups to get available WLAN group IDs.');
89
89
  }
90
90
 
91
91
  const resolvedSiteId = this.site.resolveSiteId(siteId);
@@ -102,10 +102,10 @@ export class NetworkOperations {
102
102
  */
103
103
  public async getSsidDetail(wlanId: string, ssidId: string, siteId?: string): Promise<unknown> {
104
104
  if (!wlanId) {
105
- throw new Error('A wlanId must be provided. Use getWlanGroupList to get available WLAN group IDs.');
105
+ throw new Error('A wlanId must be provided. Use omada_read at path /wifi/groups to get available WLAN group IDs.');
106
106
  }
107
107
  if (!ssidId) {
108
- throw new Error('An ssidId must be provided. Use getSsidList to get available SSID IDs.');
108
+ throw new Error('An ssidId must be provided. Use omada_read at path /wifi/ssids to get available SSID IDs.');
109
109
  }
110
110
 
111
111
  const resolvedSiteId = this.site.resolveSiteId(siteId);
@@ -61,6 +61,6 @@ export class SiteOperations {
61
61
  return this.defaultSiteId;
62
62
  }
63
63
 
64
- throw new Error('A site id must be provided either in the environment or as a parameter.');
64
+ throw new Error('A site id must be provided either in the environment or as a parameter. Use omada_browse at path / to discover available sites and their IDs.');
65
65
  }
66
66
  }
@@ -74,7 +74,7 @@ export function registerAllResources(server: McpServer, client: HaClient): void
74
74
  {
75
75
  uri: uri.href,
76
76
  mimeType: 'application/json',
77
- text: JSON.stringify({ error: 'Entity not found', entity_id: entityId }),
77
+ text: JSON.stringify({ error: 'Entity not found', entity_id: entityId, hint: 'Use the searchEntities tool with a keyword to find the correct entity_id' }),
78
78
  },
79
79
  ],
80
80
  };
@@ -115,7 +115,7 @@ export function registerAllResources(server: McpServer, client: HaClient): void
115
115
  {
116
116
  uri: uri.href,
117
117
  mimeType: 'application/json',
118
- text: JSON.stringify({ error: 'Entity not found', entity_id: entityId }),
118
+ text: JSON.stringify({ error: 'Entity not found', entity_id: entityId, hint: 'Use the searchEntities tool with a keyword to find the correct entity_id' }),
119
119
  },
120
120
  ],
121
121
  };
@@ -19,7 +19,7 @@ export function registerEntityActionTool(server: McpServer, client: HaClient): v
19
19
  wrapToolHandler('entityAction', async ({ entity_id, action }: EntityActionArgs) => {
20
20
  const domain = entity_id.split('.')[0];
21
21
  if (!domain) {
22
- return toToolResult({ error: 'Invalid entity_id format' }, true);
22
+ return toToolResult({ error: 'Invalid entity_id format', hint: 'entity_id must be in domain.name format such as light.living_room — use the searchEntities tool to discover valid entity IDs' }, true);
23
23
  }
24
24
  const result = await client.callService({
25
25
  domain,
@@ -16,7 +16,7 @@ export function registerGetStateTool(server: McpServer, client: HaClient): void
16
16
  wrapToolHandler('getState', async ({ entity_id }: { entity_id: string }) => {
17
17
  const state = await client.getState(entity_id);
18
18
  if (!state) {
19
- return toToolResult({ error: 'Entity not found', entity_id }, true);
19
+ return toToolResult({ error: 'Entity not found', entity_id, hint: 'Use the searchEntities tool with a keyword to find the correct entity_id' }, true);
20
20
  }
21
21
  return toToolResult(state);
22
22
  }, Permission.QUERY)
package/src/version.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * Package version - hardcoded at build time for reliability
3
3
  * Keep in sync with package.json version
4
4
  */
5
- export const VERSION = '1.5.3';
5
+ export const VERSION = '1.5.5';