@cyanheads/pixoo-mcp-server 1.1.2 → 1.1.4
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 +36 -19
- package/CLAUDE.md +36 -19
- package/README.md +9 -8
- package/changelog/1.1.x/1.1.3.md +30 -0
- package/changelog/1.1.x/1.1.4.md +22 -0
- package/changelog/template.md +7 -7
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-compose-scene.tool.d.ts +11 -1
- package/dist/mcp-server/tools/definitions/pixoo-compose-scene.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-compose-scene.tool.js +16 -6
- package/dist/mcp-server/tools/definitions/pixoo-compose-scene.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-control-device.tool.d.ts +4 -12
- package/dist/mcp-server/tools/definitions/pixoo-control-device.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-control-device.tool.js +21 -33
- package/dist/mcp-server/tools/definitions/pixoo-control-device.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-design-brief.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-discover-devices.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/pixoo-discover-devices.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-discover-devices.tool.js +1 -0
- package/dist/mcp-server/tools/definitions/pixoo-discover-devices.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-display-text.tool.d.ts +10 -1
- package/dist/mcp-server/tools/definitions/pixoo-display-text.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-display-text.tool.js +12 -2
- package/dist/mcp-server/tools/definitions/pixoo-display-text.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-overlay-text.tool.d.ts +7 -1
- package/dist/mcp-server/tools/definitions/pixoo-overlay-text.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-overlay-text.tool.js +24 -10
- package/dist/mcp-server/tools/definitions/pixoo-overlay-text.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-push-image.tool.d.ts +9 -0
- package/dist/mcp-server/tools/definitions/pixoo-push-image.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-push-image.tool.js +11 -1
- package/dist/mcp-server/tools/definitions/pixoo-push-image.tool.js.map +1 -1
- package/dist/renderer/remote-image.d.ts.map +1 -1
- package/dist/renderer/remote-image.js +22 -6
- package/dist/renderer/remote-image.js.map +1 -1
- package/dist/renderer/scene-renderer.d.ts.map +1 -1
- package/dist/renderer/scene-renderer.js +26 -3
- package/dist/renderer/scene-renderer.js.map +1 -1
- package/dist/services/pixoo/pixoo-service.d.ts +14 -1
- package/dist/services/pixoo/pixoo-service.d.ts.map +1 -1
- package/dist/services/pixoo/pixoo-service.js +38 -15
- package/dist/services/pixoo/pixoo-service.js.map +1 -1
- package/package.json +6 -6
- package/server.json +3 -3
package/AGENTS.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** pixoo-mcp-server
|
|
4
|
-
**Version:** 1.1.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 1.1.4
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revision 2026-07-28 alongside the 2025 era)
|
|
8
|
-
**Zod:** ^4.6.
|
|
8
|
+
**Zod:** ^4.6.5
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -165,6 +165,7 @@ Handlers receive a unified `ctx` object. Key properties used by this server:
|
|
|
165
165
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
|
|
166
166
|
| `ctx.enrich` | Success-path agent context — `.notice()` / `.total()` / `.echo()` / `.truncated()`. Lands only when the definition declares an `enrichment` block. |
|
|
167
167
|
| `ctx.fail` | Typed throw against the definition's `errors[]` reason union — auto-populates `data.reason`. |
|
|
168
|
+
| `ctx.recoveryFor` | `{ recovery: { hint } }` for a declared reason, resolved from the contract. Pass it as `ctx.fail`'s data argument (or spread it in) to put the declared hint on the wire. |
|
|
168
169
|
| `ctx.state` | Tenant-scoped KV — `.get`, `.set(key, value, { ttl? })`, `.delete`, `.getMany`, `.list`. Keys are validated; colons are not legal separators. |
|
|
169
170
|
| `ctx.requestInput` / `ctx.inputs` | Multi-round-trip input. `return ctx.requestInput(...)`; read the answers with `ctx.inputs.accepted(key, schema)` on re-entry. Unused by this server. |
|
|
170
171
|
| `ctx.signal` | `AbortSignal` for cancellation. Pass it to any long-running I/O. |
|
|
@@ -179,28 +180,36 @@ Handlers throw — the framework catches, classifies, and formats.
|
|
|
179
180
|
|
|
180
181
|
Pixoo-specific error reasons declared on tools:
|
|
181
182
|
|
|
182
|
-
| Reason | Code | When |
|
|
183
|
-
|
|
184
|
-
| `device_unreachable` | `ServiceUnavailable` | Toolkit result kind `network`/`timeout` |
|
|
185
|
-
| `device_http_error` | `ServiceUnavailable` | Non-2xx from the device's HTTP server |
|
|
186
|
-
| `device_rejected` | `ServiceUnavailable` | Firmware returned non-zero `error_code` |
|
|
187
|
-
| `no_device_configured` | `InvalidParams` | Device tool called without `PIXOO_IP` |
|
|
188
|
-
| `asset_not_found` | `NotFound` | Image/sprite path or URL unreadable |
|
|
189
|
-
| `invalid_color` | `InvalidParams` | `resolveColor` throw — invalid color name or format |
|
|
190
|
-
| `unknown_icon` | `InvalidParams` | Icon name not in registry |
|
|
191
|
-
| `discovery_failed` | `ServiceUnavailable` | Divoom cloud unreachable |
|
|
183
|
+
| Reason | Code | When | `retryable` |
|
|
184
|
+
|:-------|:-----|:-----|:------------|
|
|
185
|
+
| `device_unreachable` | `ServiceUnavailable` | Toolkit result kind `network`/`timeout` | `true` |
|
|
186
|
+
| `device_http_error` | `ServiceUnavailable` | Non-2xx from the device's HTTP server | `true` for 408, 429, 500, 502–504; `false` otherwise |
|
|
187
|
+
| `device_rejected` | `ServiceUnavailable` | Firmware returned non-zero `error_code` | — |
|
|
188
|
+
| `no_device_configured` | `InvalidParams` | Device tool called without `PIXOO_IP` | — |
|
|
189
|
+
| `asset_not_found` | `NotFound` | Image/sprite path or URL unreadable | — |
|
|
190
|
+
| `invalid_color` | `InvalidParams` | `resolveColor` throw — invalid color name or format | — |
|
|
191
|
+
| `unknown_icon` | `InvalidParams` | Icon name not in registry | — |
|
|
192
|
+
| `discovery_failed` | `ServiceUnavailable` | Divoom cloud unreachable | `true` |
|
|
193
|
+
|
|
194
|
+
`PixooService` and `src/renderer/` raise the device, configuration, and asset reasons themselves (a factory error carrying `data.reason`), so those entries carry `thrownBy: 'service'` — lint-only metadata that keeps `error-contract-unthrown` from reading them as dead. A reason the handler throws with `ctx.fail` stays unmarked, and every such site forwards its declared recovery with `ctx.recoveryFor`. A computed reason forwards the same way (`ctx.recoveryFor(reason)`); `lint:mcp` skips a definition whose `ctx.fail` reason is non-literal, so a clean lint says nothing about those sites.
|
|
195
|
+
|
|
196
|
+
A contract's `retryable` reaches the wire only through `ctx.fail`; a service throw carries it only when the service writes `data.retryable` itself. `classifyDeviceFailure` (in `pixoo-service.ts`) is the one place a failed device call becomes a reason and a retryability — `PixooService` writes both on its push-path throws, and `pixoo_overlay_text` passes the retryability into `ctx.fail` so it overrides the contract default per occurrence.
|
|
192
197
|
|
|
193
198
|
```ts
|
|
194
199
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
195
200
|
|
|
196
201
|
errors: [
|
|
197
202
|
{ reason: 'no_device_configured', code: JsonRpcErrorCode.InvalidParams,
|
|
198
|
-
when: 'PIXOO_IP not
|
|
199
|
-
recovery: 'Run pixoo_discover_devices to find device
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
when: 'PIXOO_IP is not set.',
|
|
204
|
+
recovery: 'Run pixoo_discover_devices to find the device IP, then set PIXOO_IP.',
|
|
205
|
+
thrownBy: 'service' },
|
|
206
|
+
{ reason: 'invalid_color', code: JsonRpcErrorCode.InvalidParams,
|
|
207
|
+
when: 'The color value could not be resolved.',
|
|
208
|
+
recovery: 'Use a hex color (#RRGGBB or #RGB, with or without the #) or a case-insensitive named color such as white, orange, or claude.' },
|
|
203
209
|
],
|
|
210
|
+
|
|
211
|
+
// in the handler
|
|
212
|
+
throw ctx.fail('invalid_color', `Invalid color "${input.color}"`, ctx.recoveryFor('invalid_color'));
|
|
204
213
|
```
|
|
205
214
|
|
|
206
215
|
Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring. A tool argument that fails the input schema reaches the client as `InvalidParams` (-32602) with `structuredContent.error` — assert that code, not `ValidationError`, in tests.
|
|
@@ -284,7 +293,7 @@ Available skills:
|
|
|
284
293
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
285
294
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
286
295
|
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
|
|
287
|
-
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review,
|
|
296
|
+
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
|
|
288
297
|
| `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
289
298
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
290
299
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
@@ -331,6 +340,8 @@ Available skills:
|
|
|
331
340
|
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
332
341
|
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
333
342
|
|
|
343
|
+
**CI is one file.** `.github/workflows/codeql.yml` is the only GitHub Actions workflow: CodeQL is GitHub-owned end to end, and the file runs only while the repo's CodeQL *default setup* is turned off. Verification — `devcheck`, tests, the release gates — runs locally; don't add a workflow that re-runs it.
|
|
344
|
+
|
|
334
345
|
---
|
|
335
346
|
|
|
336
347
|
## Bundling
|
|
@@ -351,6 +362,12 @@ Each per-version file opens with YAML frontmatter: `summary` (required, ≤350 c
|
|
|
351
362
|
|
|
352
363
|
---
|
|
353
364
|
|
|
365
|
+
## Publishing
|
|
366
|
+
|
|
367
|
+
**Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. A caller's brief may run a given release as `gated` instead — a `release-pr-review` pass on the open PR before `release-and-publish`. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history.
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
354
371
|
## Checklist
|
|
355
372
|
|
|
356
373
|
- [ ] Zod schemas: all fields have `.describe()`, only JSON-Schema-serializable types (no `z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`)
|
package/CLAUDE.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** pixoo-mcp-server
|
|
4
|
-
**Version:** 1.1.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 1.1.4
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revision 2026-07-28 alongside the 2025 era)
|
|
8
|
-
**Zod:** ^4.6.
|
|
8
|
+
**Zod:** ^4.6.5
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -165,6 +165,7 @@ Handlers receive a unified `ctx` object. Key properties used by this server:
|
|
|
165
165
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
|
|
166
166
|
| `ctx.enrich` | Success-path agent context — `.notice()` / `.total()` / `.echo()` / `.truncated()`. Lands only when the definition declares an `enrichment` block. |
|
|
167
167
|
| `ctx.fail` | Typed throw against the definition's `errors[]` reason union — auto-populates `data.reason`. |
|
|
168
|
+
| `ctx.recoveryFor` | `{ recovery: { hint } }` for a declared reason, resolved from the contract. Pass it as `ctx.fail`'s data argument (or spread it in) to put the declared hint on the wire. |
|
|
168
169
|
| `ctx.state` | Tenant-scoped KV — `.get`, `.set(key, value, { ttl? })`, `.delete`, `.getMany`, `.list`. Keys are validated; colons are not legal separators. |
|
|
169
170
|
| `ctx.requestInput` / `ctx.inputs` | Multi-round-trip input. `return ctx.requestInput(...)`; read the answers with `ctx.inputs.accepted(key, schema)` on re-entry. Unused by this server. |
|
|
170
171
|
| `ctx.signal` | `AbortSignal` for cancellation. Pass it to any long-running I/O. |
|
|
@@ -179,28 +180,36 @@ Handlers throw — the framework catches, classifies, and formats.
|
|
|
179
180
|
|
|
180
181
|
Pixoo-specific error reasons declared on tools:
|
|
181
182
|
|
|
182
|
-
| Reason | Code | When |
|
|
183
|
-
|
|
184
|
-
| `device_unreachable` | `ServiceUnavailable` | Toolkit result kind `network`/`timeout` |
|
|
185
|
-
| `device_http_error` | `ServiceUnavailable` | Non-2xx from the device's HTTP server |
|
|
186
|
-
| `device_rejected` | `ServiceUnavailable` | Firmware returned non-zero `error_code` |
|
|
187
|
-
| `no_device_configured` | `InvalidParams` | Device tool called without `PIXOO_IP` |
|
|
188
|
-
| `asset_not_found` | `NotFound` | Image/sprite path or URL unreadable |
|
|
189
|
-
| `invalid_color` | `InvalidParams` | `resolveColor` throw — invalid color name or format |
|
|
190
|
-
| `unknown_icon` | `InvalidParams` | Icon name not in registry |
|
|
191
|
-
| `discovery_failed` | `ServiceUnavailable` | Divoom cloud unreachable |
|
|
183
|
+
| Reason | Code | When | `retryable` |
|
|
184
|
+
|:-------|:-----|:-----|:------------|
|
|
185
|
+
| `device_unreachable` | `ServiceUnavailable` | Toolkit result kind `network`/`timeout` | `true` |
|
|
186
|
+
| `device_http_error` | `ServiceUnavailable` | Non-2xx from the device's HTTP server | `true` for 408, 429, 500, 502–504; `false` otherwise |
|
|
187
|
+
| `device_rejected` | `ServiceUnavailable` | Firmware returned non-zero `error_code` | — |
|
|
188
|
+
| `no_device_configured` | `InvalidParams` | Device tool called without `PIXOO_IP` | — |
|
|
189
|
+
| `asset_not_found` | `NotFound` | Image/sprite path or URL unreadable | — |
|
|
190
|
+
| `invalid_color` | `InvalidParams` | `resolveColor` throw — invalid color name or format | — |
|
|
191
|
+
| `unknown_icon` | `InvalidParams` | Icon name not in registry | — |
|
|
192
|
+
| `discovery_failed` | `ServiceUnavailable` | Divoom cloud unreachable | `true` |
|
|
193
|
+
|
|
194
|
+
`PixooService` and `src/renderer/` raise the device, configuration, and asset reasons themselves (a factory error carrying `data.reason`), so those entries carry `thrownBy: 'service'` — lint-only metadata that keeps `error-contract-unthrown` from reading them as dead. A reason the handler throws with `ctx.fail` stays unmarked, and every such site forwards its declared recovery with `ctx.recoveryFor`. A computed reason forwards the same way (`ctx.recoveryFor(reason)`); `lint:mcp` skips a definition whose `ctx.fail` reason is non-literal, so a clean lint says nothing about those sites.
|
|
195
|
+
|
|
196
|
+
A contract's `retryable` reaches the wire only through `ctx.fail`; a service throw carries it only when the service writes `data.retryable` itself. `classifyDeviceFailure` (in `pixoo-service.ts`) is the one place a failed device call becomes a reason and a retryability — `PixooService` writes both on its push-path throws, and `pixoo_overlay_text` passes the retryability into `ctx.fail` so it overrides the contract default per occurrence.
|
|
192
197
|
|
|
193
198
|
```ts
|
|
194
199
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
195
200
|
|
|
196
201
|
errors: [
|
|
197
202
|
{ reason: 'no_device_configured', code: JsonRpcErrorCode.InvalidParams,
|
|
198
|
-
when: 'PIXOO_IP not
|
|
199
|
-
recovery: 'Run pixoo_discover_devices to find device
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
when: 'PIXOO_IP is not set.',
|
|
204
|
+
recovery: 'Run pixoo_discover_devices to find the device IP, then set PIXOO_IP.',
|
|
205
|
+
thrownBy: 'service' },
|
|
206
|
+
{ reason: 'invalid_color', code: JsonRpcErrorCode.InvalidParams,
|
|
207
|
+
when: 'The color value could not be resolved.',
|
|
208
|
+
recovery: 'Use a hex color (#RRGGBB or #RGB, with or without the #) or a case-insensitive named color such as white, orange, or claude.' },
|
|
203
209
|
],
|
|
210
|
+
|
|
211
|
+
// in the handler
|
|
212
|
+
throw ctx.fail('invalid_color', `Invalid color "${input.color}"`, ctx.recoveryFor('invalid_color'));
|
|
204
213
|
```
|
|
205
214
|
|
|
206
215
|
Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring. A tool argument that fails the input schema reaches the client as `InvalidParams` (-32602) with `structuredContent.error` — assert that code, not `ValidationError`, in tests.
|
|
@@ -284,7 +293,7 @@ Available skills:
|
|
|
284
293
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
285
294
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
286
295
|
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
|
|
287
|
-
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review,
|
|
296
|
+
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
|
|
288
297
|
| `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
289
298
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
290
299
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
@@ -331,6 +340,8 @@ Available skills:
|
|
|
331
340
|
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
332
341
|
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
333
342
|
|
|
343
|
+
**CI is one file.** `.github/workflows/codeql.yml` is the only GitHub Actions workflow: CodeQL is GitHub-owned end to end, and the file runs only while the repo's CodeQL *default setup* is turned off. Verification — `devcheck`, tests, the release gates — runs locally; don't add a workflow that re-runs it.
|
|
344
|
+
|
|
334
345
|
---
|
|
335
346
|
|
|
336
347
|
## Bundling
|
|
@@ -351,6 +362,12 @@ Each per-version file opens with YAML frontmatter: `summary` (required, ≤350 c
|
|
|
351
362
|
|
|
352
363
|
---
|
|
353
364
|
|
|
365
|
+
## Publishing
|
|
366
|
+
|
|
367
|
+
**Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. A caller's brief may run a given release as `gated` instead — a `release-pr-review` pass on the open PR before `release-and-publish`. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history.
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
354
371
|
## Checklist
|
|
355
372
|
|
|
356
373
|
- [ ] Zod schemas: all fields have `.describe()`, only JSON-Schema-serializable types (no `z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`)
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/pixoo-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/pixoo-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -66,7 +66,8 @@ All resource data is also reachable via tools. `pixoo_design_brief` surfaces the
|
|
|
66
66
|
- Up to 50 layered elements rendered back-to-front: `text`, `icon`, `rect`, `circle`, `line`, `progress`, `sparkline`, `bitmap`, `pixels`, `image`, `sprite`
|
|
67
67
|
- Background: solid color, gradient (vertical, horizontal, or radial), or named theme
|
|
68
68
|
- Animation via named effect presets (`float`, `scroll-left`, `scroll-right`, `pulse`, `blink`, `twinkle`, `drift`, `fade-in`, `fade-out`) or raw per-property keyframe arrays — 1–40 frames at 10–2000ms per frame (default 150ms)
|
|
69
|
-
- `image`
|
|
69
|
+
- `image` elements accept an absolute local path or an https URL; `sprite` elements take an absolute local path; a supplied `output` path must be absolute with no traversal segments
|
|
70
|
+
- `opacity` (0–100) blends any element, images included, over the layers beneath it
|
|
70
71
|
- Static scenes return a PNG preview; animations return a labeled contact-sheet PNG plus a saved GIF (GIF preview is inconsistent across MCP clients)
|
|
71
72
|
- Typed failures for `asset_not_found`, `invalid_color`, and `unknown_icon`, alongside the shared device-error reasons
|
|
72
73
|
|
|
@@ -74,7 +75,7 @@ All resource data is also reachable via tools. `pixoo_design_brief` surfaces the
|
|
|
74
75
|
|
|
75
76
|
### `pixoo_push_image` <sub>tool</sub>
|
|
76
77
|
|
|
77
|
-
- Accepts an absolute local file path or an https (not http) URL
|
|
78
|
+
- Accepts an absolute local file path or an https (not http) URL; a URL response is capped at 10 MB, enforced while the body streams
|
|
78
79
|
- Three fit modes: `contain` (letterbox), `cover` (crop to fill), `fill` (stretch)
|
|
79
80
|
- Three resize kernels: `nearest` for pixel art (default), `lanczos3` for photos, `mitchell` for a balance
|
|
80
81
|
- Returns the exact resized result as an image content block before it is pushed
|
|
@@ -85,7 +86,7 @@ All resource data is also reachable via tools. `pixoo_design_brief` surfaces the
|
|
|
85
86
|
|
|
86
87
|
- `mode: "set"` adds or updates an overlay on one of 20 independent slots (`id` 0–19); `mode: "clear"` removes it
|
|
87
88
|
- 115 device-rendered font IDs (0–114); overlays persist across channel switches until explicitly cleared
|
|
88
|
-
- Configurable `x`/`y` (0–64), scroll `direction` (`left`/`right`), `speed` (0–100), and `align`; color
|
|
89
|
+
- Configurable `x`/`y` (0–64), scroll `direction` (`left`/`right`), `speed` (0–100), and `align`; color accepts hex (`#RRGGBB` or `#RGB`) or a named color, the same as the render tools
|
|
89
90
|
- Device-rendered, not previewable — for styled, previewable text use `pixoo_display_text`
|
|
90
91
|
|
|
91
92
|
---
|
|
@@ -93,7 +94,7 @@ All resource data is also reachable via tools. `pixoo_design_brief` surfaces the
|
|
|
93
94
|
### `pixoo_control_device` <sub>tool</sub>
|
|
94
95
|
|
|
95
96
|
- Call with no params to read state only; supply any of `brightness` (0–100), `screen` (`on`/`off`), `channel` (`faces`/`cloud`/`visualizer`/`custom`), or `clockFaceId` to apply changes before the read-back
|
|
96
|
-
- `applied` lists which requested settings succeeded; a failed setting is omitted from `applied` and reported
|
|
97
|
+
- `applied` lists which requested settings succeeded; a failed setting is omitted from `applied` and reported in a `notice` instead of failing the call — every failed setting in one call, each with its failure kind and message
|
|
97
98
|
- Always returns current `reachable`, `channel`, `brightness`, `screenOn`, and `clockId` (the latter three absent when the device is unreachable)
|
|
98
99
|
|
|
99
100
|
---
|
|
@@ -162,8 +163,8 @@ Agent-friendly output:
|
|
|
162
163
|
|
|
163
164
|
- Preview-as-content — render tools return the upscaled (8×, 512px) output as an image content block, so the calling model sees exactly what was drawn, before and after push
|
|
164
165
|
- Layout transparency — every silent renderer decision (font fallback, truncation, scroll engaged, element clipped) is reported in `layout[]` so agents can inspect and refine
|
|
165
|
-
- Device truth — `pushed` reflects the device ACK; `deviceState` after a push
|
|
166
|
-
-
|
|
166
|
+
- Device truth — `pushed` reflects the device ACK; `deviceState` after a push reports the channel, brightness, and screen state the device returned
|
|
167
|
+
- Render without a device — `push: false` renders and returns the preview with no device reachable; a push to an unreachable device fails with a retryable `device_unreachable` error
|
|
167
168
|
|
|
168
169
|
## Getting started
|
|
169
170
|
|
|
@@ -275,7 +276,7 @@ All configuration is validated at startup via Zod schemas in `src/config/server-
|
|
|
275
276
|
| `PIXOO_PUSH_MIN_INTERVAL_MS` | Minimum interval between device pushes in milliseconds. Prevents device freeze from rapid-fire commands. | `1000` |
|
|
276
277
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
277
278
|
| `MCP_HTTP_PORT` | HTTP server port. | `3010` |
|
|
278
|
-
| `MCP_SESSION_MODE` | HTTP session handling: `stateful`, `stateless`, or `auto
|
|
279
|
+
| `MCP_SESSION_MODE` | HTTP session handling: `stateful`, `stateless`, or `auto` (the framework's schema default, which resolves to `stateful`). The server declares `stateless` in source — no tool requests input mid-call — and a value set here overrides it. | `stateless` |
|
|
279
280
|
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
|
|
280
281
|
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
|
|
281
282
|
| `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Errors thrown in the tool handlers now forward their declared recovery hint to callers; invalid_color hints match what resolveColor accepts; pixoo_control_device drops two error reasons it could never emit (#8). mcp-ts-core ^0.13.2 → ^0.13.6 adds a Recovery: hint on argument rejections and key normalization for tool calls."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 1.1.3 — 2026-09-21
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **Declared recovery hints reach the caller** — errors thrown in the tool handlers (`invalid_color`, `unknown_icon`, `invalid_output_path`, local-path `asset_not_found`, and `pixoo_overlay_text`'s `device_unreachable`/`device_rejected`) now carry `error.data.recovery.hint` and a `Recovery:` line in the text; previously those errors carried only `data.reason`.
|
|
12
|
+
- **`invalid_color` hints match what `resolveColor` accepts** — hex (`#RRGGBB`/`#RGB`, `#` optional) or a case-insensitive named color. `pixoo_overlay_text`'s hint used to say named colors were unsupported for overlays, but its handler always resolved them (#8).
|
|
13
|
+
- **`pixoo_control_device` no longer declares `device_unreachable`/`device_rejected`** — its setters report failures in the result instead of throwing, so neither reason could occur; only the advertised `reason` examples change.
|
|
14
|
+
- `pixoo_design_brief`'s color-troubleshooting topic now says named colors are case-insensitive, matching `resolveColor`.
|
|
15
|
+
- Server `instructions` reworded to one two-sentence string, following the framework's instructions-shape guidance.
|
|
16
|
+
- Service-raised error reasons are marked `thrownBy: 'service'` in each tool's error contract — lint metadata only, no wire effect.
|
|
17
|
+
- **Argument rejections carry a schema-derived `Recovery:` hint** naming the tool's accepted keys, and an omitted required enum/literal field now reads as missing rather than as a wrong choice ([cyanheads/mcp-ts-core#445](https://github.com/cyanheads/mcp-ts-core/issues/445), [cyanheads/mcp-ts-core#378](https://github.com/cyanheads/mcp-ts-core/issues/378)).
|
|
18
|
+
- **A case-style variant of a declared argument key is rewritten and accepted** (e.g. `clock_face_id` for `clockFaceId`), and a JSON-stringified array argument is repaired and re-parsed once; an undeclared key is still rejected ([cyanheads/mcp-ts-core#452](https://github.com/cyanheads/mcp-ts-core/issues/452), [cyanheads/mcp-ts-core#453](https://github.com/cyanheads/mcp-ts-core/issues/453), [cyanheads/mcp-ts-core#234](https://github.com/cyanheads/mcp-ts-core/issues/234)).
|
|
19
|
+
- **Tool error text now closes with `(reason <reason>)`**, plus ` · retryable` / ` · not retryable` when the error declares `data.retryable` ([cyanheads/mcp-ts-core#458](https://github.com/cyanheads/mcp-ts-core/issues/458)).
|
|
20
|
+
- `.env.example`/README now note that `MCP_SESSION_MODE=auto` (the framework's schema default) resolves to `stateful`; the shipped value stays `stateless`. `.env.example` documents the commented `MCP_HTTP_RESUMABILITY*` options.
|
|
21
|
+
- Test config hides `pixoo-toolkit`'s dangling sourcemap warnings during tests ([cyanheads/pixoo-toolkit#42](https://github.com/cyanheads/pixoo-toolkit/issues/42)).
|
|
22
|
+
- Repo hygiene: CodeQL workflow added; framework skills, `lint-mcp.ts`, the changelog template, and `devcheck.config.json` synced with mcp-ts-core 0.13.6.
|
|
23
|
+
|
|
24
|
+
## Dependencies
|
|
25
|
+
|
|
26
|
+
- `@cyanheads/mcp-ts-core` ^0.13.2 → ^0.13.6
|
|
27
|
+
- `zod` ^4.6.4 → ^4.6.5
|
|
28
|
+
- `@biomejs/biome` 2.5.13 → 2.5.14
|
|
29
|
+
- `@types/node` 26.5.1 → 26.6.2
|
|
30
|
+
- `vitest` ^5.0.0 → ^5.0.1
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Push-capable tools now declare device_http_error and carry retryable; pixoo_compose_scene's image opacity and missing local-asset errors are fixed; pixoo_control_device no longer drops failed-setter notices; pixoo_push_image's fetch cap now holds while the body streams."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 1.1.4 — 2026-09-23
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- Entry-point session-mode test retries on a fresh port when the child exits before serving, instead of waiting out the full boot deadline.
|
|
12
|
+
|
|
13
|
+
## Fixed
|
|
14
|
+
|
|
15
|
+
- **`pixoo_display_text`, `pixoo_compose_scene`, and `pixoo_push_image` now declare `device_http_error` and carry `data.retryable` on push failures** — `mapFailure` threw through the raw `serviceUnavailable()` factory, bypassing the tool contract ([#11](https://github.com/cyanheads/pixoo-mcp-server/issues/11)).
|
|
16
|
+
- **`pixoo_compose_scene`'s `image` elements honor `opacity`** — the blit went to the base canvas instead of the opacity-compositing scratch canvas, so opacity was silently dropped ([#4](https://github.com/cyanheads/pixoo-mcp-server/issues/4)).
|
|
17
|
+
- **A missing local `image` or `sprite` path in `pixoo_compose_scene` now fails as `asset_not_found`** instead of an unclassified `InternalError` ([#12](https://github.com/cyanheads/pixoo-mcp-server/issues/12)).
|
|
18
|
+
- **`pixoo_control_device` no longer drops failed-setter notices** — the tool declared no `enrichment` block, so `ctx.enrich.notice` calls were discarded; failures across multiple setters in one call now compose into a single notice ([#13](https://github.com/cyanheads/pixoo-mcp-server/issues/13)).
|
|
19
|
+
|
|
20
|
+
## Security
|
|
21
|
+
|
|
22
|
+
- **`pixoo_push_image`'s 10 MB fetch cap now holds while the body streams** — a URL response with no `content-length` header was fully buffered before the size check fired, allowing unbounded memory use ([#7](https://github.com/cyanheads/pixoo-mcp-server/issues/7)).
|
package/changelog/template.md
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
# Required. One-line GitHub Release-style headline. 350 character cap — a
|
|
8
8
|
# ceiling, not a target. Default short and scannable. Don't pad, don't stitch
|
|
9
|
-
# unrelated changes with commas/semicolons into an inventory — pick the
|
|
10
|
-
# headline
|
|
9
|
+
# unrelated changes with commas/semicolons into an inventory — pick the one
|
|
10
|
+
# headline the release is about. Quotes required: unquoted YAML treats
|
|
11
11
|
# `: ` inside the value as a key separator and fails GitHub's strict parser.
|
|
12
12
|
summary: ""
|
|
13
13
|
|
|
@@ -119,11 +119,11 @@ security: false
|
|
|
119
119
|
TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
|
|
120
120
|
via `gh release create --notes-from-tag`. It is a condensed digest of this
|
|
121
121
|
entry, never a copy, and its format is owned by the `release-and-publish`
|
|
122
|
-
skill (step 4, "Create the annotated tag"):
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
skill (step 4, "Create the annotated tag"): a short subject line without the
|
|
123
|
+
version, flat headline bullets — no Keep-a-Changelog section headers, no
|
|
124
|
+
gates line — at most one deps line, issue backlinks, and the changelog link
|
|
125
|
+
last. In release-PR mode the `git-wrapup` skill authors those bullets as the
|
|
126
|
+
PR body's `## Changes` and the tag copies them.
|
|
127
127
|
-->
|
|
128
128
|
|
|
129
129
|
## Added
|
package/dist/index.js
CHANGED
|
@@ -53,8 +53,6 @@ await createApp({
|
|
|
53
53
|
setup(core) {
|
|
54
54
|
initPixooService(core.config, core.storage);
|
|
55
55
|
},
|
|
56
|
-
instructions: '
|
|
57
|
-
'pixoo_display_text is the 80% case for styled text. pixoo_compose_scene for layered scenes, widgets, and animations. ' +
|
|
58
|
-
'All render tools return a preview image so you can inspect the result before it hits the display.',
|
|
56
|
+
instructions: 'Run pixoo_design_brief with a topic first for craft guidance and live device state, then render with pixoo_display_text for styled text or pixoo_compose_scene for layered scenes, widgets, and animations. Every render tool returns a preview image, so pass push: false to inspect a design before it reaches the Pixoo display.',
|
|
59
57
|
});
|
|
60
58
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY;AACZ,OAAO,EAAE,wBAAwB,EAAE,MAAM,mEAAmE,CAAC;AAC7G,OAAO,EAAE,yBAAyB,EAAE,MAAM,oEAAoE,CAAC;AAC/G,OAAO,EAAE,kBAAkB,EAAE,MAAM,4DAA4D,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AAClG,QAAQ;AACR,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+DAA+D,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,yDAAyD,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,kBAAkB;IACzB,KAAK,EAAE;QACL,gBAAgB;QAChB,iBAAiB;QACjB,cAAc;QACd,gBAAgB;QAChB,kBAAkB;QAClB,oBAAoB;QACpB,gBAAgB;KACjB;IACD,SAAS,EAAE;QACT,yBAAyB;QACzB,mBAAmB;QACnB,kBAAkB;QAClB,wBAAwB;KACzB;IACD,OAAO,EAAE,EAAE;IACX,2EAA2E;IAC3E,WAAW,EAAE,WAAW;IACxB;;;;;OAKG;IACH,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QACxD,gBAAgB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC5D,0BAA0B,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;KACvE;IACD,KAAK,CAAC,IAAI;QACR,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IACD,YAAY,EACV,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY;AACZ,OAAO,EAAE,wBAAwB,EAAE,MAAM,mEAAmE,CAAC;AAC7G,OAAO,EAAE,yBAAyB,EAAE,MAAM,oEAAoE,CAAC;AAC/G,OAAO,EAAE,kBAAkB,EAAE,MAAM,4DAA4D,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AAClG,QAAQ;AACR,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+DAA+D,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,yDAAyD,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,kBAAkB;IACzB,KAAK,EAAE;QACL,gBAAgB;QAChB,iBAAiB;QACjB,cAAc;QACd,gBAAgB;QAChB,kBAAkB;QAClB,oBAAoB;QACpB,gBAAgB;KACjB;IACD,SAAS,EAAE;QACT,yBAAyB;QACzB,mBAAmB;QACnB,kBAAkB;QAClB,wBAAwB;KACzB;IACD,OAAO,EAAE,EAAE;IACX,2EAA2E;IAC3E,WAAW,EAAE,WAAW;IACxB;;;;;OAKG;IACH,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QACxD,gBAAgB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC5D,0BAA0B,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;KACvE;IACD,KAAK,CAAC,IAAI;QACR,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IACD,YAAY,EACV,qUAAqU;CACxU,CAAC,CAAC"}
|
|
@@ -471,26 +471,36 @@ export declare const pixooComposeScene: import("@cyanheads/mcp-ts-core").ToolDef
|
|
|
471
471
|
readonly when: "Device is not reachable over the network.";
|
|
472
472
|
readonly retryable: true;
|
|
473
473
|
readonly recovery: "Check the device is powered on and on the same network. Retry in a few seconds.";
|
|
474
|
+
readonly thrownBy: "service";
|
|
475
|
+
}, {
|
|
476
|
+
readonly reason: "device_http_error";
|
|
477
|
+
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
478
|
+
readonly when: "The device answered with a non-2xx HTTP status (retryable for 408, 429, 500, and 502–504).";
|
|
479
|
+
readonly recovery: "The device may be busy or rebooting; wait a few seconds and retry. If it persists, run pixoo_discover_devices to confirm PIXOO_IP points at the Pixoo.";
|
|
480
|
+
readonly thrownBy: "service";
|
|
474
481
|
}, {
|
|
475
482
|
readonly reason: "device_rejected";
|
|
476
483
|
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
477
484
|
readonly when: "Device firmware returned a non-zero error code.";
|
|
478
485
|
readonly recovery: "Note the device error code and check the Pixoo documentation.";
|
|
486
|
+
readonly thrownBy: "service";
|
|
479
487
|
}, {
|
|
480
488
|
readonly reason: "no_device_configured";
|
|
481
489
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
482
490
|
readonly when: "PIXOO_IP is not set and push was requested.";
|
|
483
491
|
readonly recovery: "Run pixoo_discover_devices to find the device IP, then set PIXOO_IP.";
|
|
492
|
+
readonly thrownBy: "service";
|
|
484
493
|
}, {
|
|
485
494
|
readonly reason: "asset_not_found";
|
|
486
495
|
readonly code: JsonRpcErrorCode.NotFound;
|
|
487
496
|
readonly when: "An image or sprite path could not be read.";
|
|
488
497
|
readonly recovery: "Verify the file path exists and is readable, or check the URL is reachable.";
|
|
498
|
+
readonly thrownBy: "service";
|
|
489
499
|
}, {
|
|
490
500
|
readonly reason: "invalid_color";
|
|
491
501
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
492
502
|
readonly when: "A color value could not be resolved.";
|
|
493
|
-
readonly recovery: "Use #RRGGBB
|
|
503
|
+
readonly recovery: "Use a hex color (#RRGGBB or #RGB, with or without the #) or a case-insensitive named color such as white, orange, or claude. See pixoo://reference/themes for palette colors.";
|
|
494
504
|
}, {
|
|
495
505
|
readonly reason: "unknown_icon";
|
|
496
506
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pixoo-compose-scene.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/pixoo-compose-scene.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAmT3E,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"pixoo-compose-scene.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/pixoo-compose-scene.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAmT3E,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgU5B,CAAC"}
|
|
@@ -323,8 +323,7 @@ export const pixooComposeScene = tool('pixoo_compose_scene', {
|
|
|
323
323
|
output: z
|
|
324
324
|
.string()
|
|
325
325
|
.optional()
|
|
326
|
-
.describe('Absolute, already-normalized path to save the first frame to, in addition to the '
|
|
327
|
-
'PIXOO_OUTPUT_DIR auto-save when that is configured. Both paths are reported in outputFiles.'),
|
|
326
|
+
.describe('Absolute, already-normalized path to save the first frame to, in addition to the PIXOO_OUTPUT_DIR auto-save when that is configured. Both paths are reported in outputFiles.'),
|
|
328
327
|
}),
|
|
329
328
|
output: z.object({
|
|
330
329
|
pushed: z
|
|
@@ -371,30 +370,41 @@ export const pixooComposeScene = tool('pixoo_compose_scene', {
|
|
|
371
370
|
when: 'Device is not reachable over the network.',
|
|
372
371
|
retryable: true,
|
|
373
372
|
recovery: 'Check the device is powered on and on the same network. Retry in a few seconds.',
|
|
373
|
+
thrownBy: 'service',
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
reason: 'device_http_error',
|
|
377
|
+
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
378
|
+
when: 'The device answered with a non-2xx HTTP status (retryable for 408, 429, 500, and 502–504).',
|
|
379
|
+
recovery: 'The device may be busy or rebooting; wait a few seconds and retry. If it persists, run pixoo_discover_devices to confirm PIXOO_IP points at the Pixoo.',
|
|
380
|
+
thrownBy: 'service',
|
|
374
381
|
},
|
|
375
382
|
{
|
|
376
383
|
reason: 'device_rejected',
|
|
377
384
|
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
378
385
|
when: 'Device firmware returned a non-zero error code.',
|
|
379
386
|
recovery: 'Note the device error code and check the Pixoo documentation.',
|
|
387
|
+
thrownBy: 'service',
|
|
380
388
|
},
|
|
381
389
|
{
|
|
382
390
|
reason: 'no_device_configured',
|
|
383
391
|
code: JsonRpcErrorCode.InvalidParams,
|
|
384
392
|
when: 'PIXOO_IP is not set and push was requested.',
|
|
385
393
|
recovery: 'Run pixoo_discover_devices to find the device IP, then set PIXOO_IP.',
|
|
394
|
+
thrownBy: 'service',
|
|
386
395
|
},
|
|
387
396
|
{
|
|
388
397
|
reason: 'asset_not_found',
|
|
389
398
|
code: JsonRpcErrorCode.NotFound,
|
|
390
399
|
when: 'An image or sprite path could not be read.',
|
|
391
400
|
recovery: 'Verify the file path exists and is readable, or check the URL is reachable.',
|
|
401
|
+
thrownBy: 'service',
|
|
392
402
|
},
|
|
393
403
|
{
|
|
394
404
|
reason: 'invalid_color',
|
|
395
405
|
code: JsonRpcErrorCode.InvalidParams,
|
|
396
406
|
when: 'A color value could not be resolved.',
|
|
397
|
-
recovery: 'Use #RRGGBB
|
|
407
|
+
recovery: 'Use a hex color (#RRGGBB or #RGB, with or without the #) or a case-insensitive named color such as white, orange, or claude. See pixoo://reference/themes for palette colors.',
|
|
398
408
|
},
|
|
399
409
|
{
|
|
400
410
|
reason: 'unknown_icon',
|
|
@@ -430,7 +440,7 @@ export const pixooComposeScene = tool('pixoo_compose_scene', {
|
|
|
430
440
|
// collapse traversal segments, passing everything.
|
|
431
441
|
if (input.output &&
|
|
432
442
|
(!path.isAbsolute(input.output) || path.normalize(input.output) !== input.output)) {
|
|
433
|
-
throw ctx.fail('invalid_output_path', `Invalid output path: "${input.output}". Must be an absolute path with no traversal segments
|
|
443
|
+
throw ctx.fail('invalid_output_path', `Invalid output path: "${input.output}". Must be an absolute path with no traversal segments.`, ctx.recoveryFor('invalid_output_path'));
|
|
434
444
|
}
|
|
435
445
|
ctx.log.info('Rendering scene', {
|
|
436
446
|
elements: input.elements.length,
|
|
@@ -449,10 +459,10 @@ export const pixooComposeScene = tool('pixoo_compose_scene', {
|
|
|
449
459
|
}
|
|
450
460
|
catch (err) {
|
|
451
461
|
if (err instanceof Error && err.message.includes('Unknown color')) {
|
|
452
|
-
throw ctx.fail('invalid_color', `${err.message}. Valid named colors: ${validColorNames}.
|
|
462
|
+
throw ctx.fail('invalid_color', `${err.message}. Valid named colors: ${validColorNames}.`, ctx.recoveryFor('invalid_color'));
|
|
453
463
|
}
|
|
454
464
|
if (err instanceof McpError && err.data?.['reason'] === 'unknown_icon') {
|
|
455
|
-
throw ctx.fail('unknown_icon', `${err.message} Valid icons: ${validIconNames}.
|
|
465
|
+
throw ctx.fail('unknown_icon', `${err.message} Valid icons: ${validIconNames}.`, ctx.recoveryFor('unknown_icon'));
|
|
456
466
|
}
|
|
457
467
|
throw err;
|
|
458
468
|
}
|