@cyanheads/pixoo-mcp-server 1.1.1 → 1.1.3
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 +93 -36
- package/CLAUDE.md +93 -36
- package/README.md +100 -71
- package/changelog/1.1.x/1.1.2.md +36 -0
- package/changelog/1.1.x/1.1.3.md +30 -0
- package/changelog/template.md +9 -26
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-compose-scene.tool.d.ts +5 -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 +9 -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 +1 -11
- 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 +1 -13
- 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 +4 -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 +5 -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 +2 -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 +9 -6
- 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 +3 -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 +4 -1
- package/dist/mcp-server/tools/definitions/pixoo-push-image.tool.js.map +1 -1
- package/package.json +11 -10
- package/server.json +3 -3
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
|
|
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## Overview
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Divoom Pixoo LED matrix displays (Pixoo-64 primary; 16 and 32 also supported) on the local network. Render and push styled text, layered scenes, dashboards, and animations, or control device state, from any MCP client. Runs as a stdio process or a local Streamable HTTP server.
|
|
27
|
+
|
|
28
|
+
### Tools
|
|
27
29
|
|
|
28
30
|
| Tool | Description |
|
|
29
31
|
|:-----|:------------|
|
|
@@ -35,87 +37,118 @@ Seven tools covering the full display pipeline — from quick styled text to ful
|
|
|
35
37
|
| `pixoo_discover_devices` | Find Pixoo devices on the local network via Divoom's cloud discovery endpoint. Run once during setup to find device IPs. |
|
|
36
38
|
| `pixoo_design_brief` | Return craft guidance and live device context for a design topic. Covers legibility rules, palette discipline, layout zones, animation budget, and pre-filled next-tool suggestions. |
|
|
37
39
|
|
|
38
|
-
###
|
|
39
|
-
|
|
40
|
-
The primary tool for text-only display. Covers the 80% case — styled text with quality defaults.
|
|
40
|
+
### Resources
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
42
|
+
| Resource | Description |
|
|
43
|
+
|:---|:---|
|
|
44
|
+
| `pixoo://device/status` | Live snapshot of the connected Pixoo display: reachable, channel, brightness, screen state, and display size |
|
|
45
|
+
| `pixoo://reference/themes` | Theme and palette registry with background gradients, default text palettes, accent colors, and swatch values |
|
|
46
|
+
| `pixoo://reference/icons` | Built-in icon names organized by category (weather, arrows, status, media) |
|
|
47
|
+
| `pixoo://reference/design-guide` | Long-form 64px craft guide: legibility floors, palette discipline, layout zones, animation budget, and known device behaviors |
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
All resource data is also reachable via tools. `pixoo_design_brief` surfaces the design guide content per topic; `pixoo_control_device` returns live device state equivalent to `pixoo://device/status`.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
## Capability reference
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
### `pixoo_display_text` <sub>tool</sub>
|
|
54
54
|
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
- Returns
|
|
55
|
+
- Named scene themes set background gradient and default text palette in one parameter (`midnight`, `ember`, `claude`, `ice`, `neon`, `forest`, `mono`)
|
|
56
|
+
- Style block: palette ramps (`ember`, `ice`, `neon`, `fire`, `lavender`, `claude`, `mono`) or a custom gradient/flat color, optional drop shadow, 1px outline, integer scale 1–8
|
|
57
|
+
- Semantic positioning (`x: "center"`, `y: "bottom"`) or absolute pixel coordinates; multi-line text stacks vertically with configurable alignment
|
|
58
|
+
- Auto-fit overflow tries standard font, then compact, then scroll; every fit decision is reported in `layout[]` with an `action` (`shrunk-to-compact`, `scrolling`, `wrapped`, `truncated`, `clipped`)
|
|
59
|
+
- Optional `brightness` (0–100) applied before push — a failure is a warning via an enrichment notice, not a tool error
|
|
60
|
+
- Returns the rendered frame as an image content block; `outputFiles` is populated only when `PIXOO_OUTPUT_DIR` is configured
|
|
61
61
|
|
|
62
62
|
---
|
|
63
63
|
|
|
64
|
-
### `
|
|
64
|
+
### `pixoo_compose_scene` <sub>tool</sub>
|
|
65
|
+
|
|
66
|
+
- Up to 50 layered elements rendered back-to-front: `text`, `icon`, `rect`, `circle`, `line`, `progress`, `sparkline`, `bitmap`, `pixels`, `image`, `sprite`
|
|
67
|
+
- Background: solid color, gradient (vertical, horizontal, or radial), or named theme
|
|
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` and `sprite` elements accept an absolute local path or an https URL; a supplied `output` path must be absolute with no traversal segments
|
|
70
|
+
- 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
|
+
- Typed failures for `asset_not_found`, `invalid_color`, and `unknown_icon`, alongside the shared device-error reasons
|
|
72
|
+
|
|
73
|
+
---
|
|
65
74
|
|
|
66
|
-
|
|
75
|
+
### `pixoo_push_image` <sub>tool</sub>
|
|
67
76
|
|
|
68
|
-
- Accepts absolute local
|
|
77
|
+
- Accepts an absolute local file path or an https (not http) URL
|
|
69
78
|
- Three fit modes: `contain` (letterbox), `cover` (crop to fill), `fill` (stretch)
|
|
70
|
-
- Three resize kernels: `nearest` for pixel art, `lanczos3` for photos, `mitchell` for a balance
|
|
71
|
-
- Returns the exact
|
|
79
|
+
- Three resize kernels: `nearest` for pixel art (default), `lanczos3` for photos, `mitchell` for a balance
|
|
80
|
+
- Returns the exact resized result as an image content block before it is pushed
|
|
72
81
|
|
|
73
82
|
---
|
|
74
83
|
|
|
75
|
-
### `pixoo_overlay_text`
|
|
84
|
+
### `pixoo_overlay_text` <sub>tool</sub>
|
|
76
85
|
|
|
77
|
-
|
|
86
|
+
- `mode: "set"` adds or updates an overlay on one of 20 independent slots (`id` 0–19); `mode: "clear"` removes it
|
|
87
|
+
- 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 is `#RRGGBB` hex only — named colors aren't supported here
|
|
89
|
+
- Device-rendered, not previewable — for styled, previewable text use `pixoo_display_text`
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
-
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### `pixoo_control_device` <sub>tool</sub>
|
|
94
|
+
|
|
95
|
+
- 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 via an enrichment notice instead of failing the call
|
|
97
|
+
- Always returns current `reachable`, `channel`, `brightness`, `screenOn`, and `clockId` (the latter three absent when the device is unreachable)
|
|
84
98
|
|
|
85
99
|
---
|
|
86
100
|
|
|
87
|
-
### `
|
|
101
|
+
### `pixoo_discover_devices` <sub>tool</sub>
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
- Queries Divoom's cloud discovery endpoint (`app.divoom-gz.com`) — requires internet access even for local device control
|
|
104
|
+
- Returns each device's name, numeric ID, and LAN IP to set as `PIXOO_IP`
|
|
105
|
+
- When `PIXOO_IP` is already configured, flags whether it matches a discovered device (`configuredIpFound`) and notes a mismatch
|
|
106
|
+
- `timeoutMs` configurable 1000–30000ms (default 5000ms)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### `pixoo_design_brief` <sub>tool</sub>
|
|
90
111
|
|
|
91
112
|
- Six topics: `text`, `scene`, `dashboard`, `animation`, `pixel-art`, `troubleshooting`
|
|
92
|
-
- Returns legibility floors, palette discipline, layout zones, animation budgets
|
|
93
|
-
-
|
|
94
|
-
-
|
|
113
|
+
- Returns markdown craft guidance (legibility floors, palette discipline, layout zones, animation budgets) plus a live `deviceContext` snapshot
|
|
114
|
+
- `nextToolSuggestions` are pre-filled with ready-to-use arguments tailored to the topic and current device state (e.g. suggests `pixoo_discover_devices` when the device is unreachable)
|
|
115
|
+
- Also returns `availableThemes` and `iconCategories` for direct use in other tools
|
|
95
116
|
|
|
96
117
|
---
|
|
97
118
|
|
|
98
|
-
|
|
119
|
+
### `pixoo://device/status` <sub>resource</sub>
|
|
99
120
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
| Resource | `pixoo://reference/themes` | Theme and palette registry with background gradients, default text palettes, accent colors, and swatch values |
|
|
104
|
-
| Resource | `pixoo://reference/icons` | Built-in icon names organized by category (weather, arrows, status, media) |
|
|
105
|
-
| Resource | `pixoo://reference/design-guide` | Long-form 64px craft guide: legibility floors, palette discipline, layout zones, animation budget, and known device behaviors |
|
|
121
|
+
- Live snapshot: `reachable`, `channel`, `brightness`, `screenOn`, `clockId`, `displaySize`, `configuredIp`
|
|
122
|
+
- No cache — every read reaches the device; degrades to `reachable: false` instead of erroring when the device is unreachable
|
|
123
|
+
- Equivalent to calling `pixoo_control_device` with no params
|
|
106
124
|
|
|
107
|
-
|
|
125
|
+
---
|
|
108
126
|
|
|
109
|
-
|
|
127
|
+
### `pixoo://reference/themes` <sub>resource</sub>
|
|
110
128
|
|
|
111
|
-
|
|
129
|
+
- Every registered theme (background gradient or solid, default text palette, accent color, shadow flag) and every named palette (gradient stop pair)
|
|
130
|
+
- `themeNames` / `paletteNames` arrays for direct use in the `theme` / `palette` parameters
|
|
131
|
+
- Compile-time constants — cached for 24h
|
|
132
|
+
|
|
133
|
+
---
|
|
112
134
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
|
|
135
|
+
### `pixoo://reference/icons` <sub>resource</sub>
|
|
136
|
+
|
|
137
|
+
- Every built-in icon name, its category, and its SVG `viewBox`, plus a `byCategory` grouping (weather, arrows, status, media)
|
|
138
|
+
- Use a `name` from this registry in `pixoo_compose_scene` icon elements
|
|
139
|
+
- Compile-time constants — cached for 24h
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### `pixoo://reference/design-guide` <sub>resource</sub>
|
|
144
|
+
|
|
145
|
+
- Long-form markdown: legibility floors, palette discipline, layout zones (top/middle/bottom strip pixel ranges), animation budget, pixel art rules, and known device behaviors (e.g. channel must be `custom` to show pushed content)
|
|
146
|
+
- `text/markdown` mime type; compile-time constant, cached for 24h
|
|
147
|
+
- Same content `pixoo_design_brief` surfaces per topic — this resource is the complete reference in one document
|
|
148
|
+
|
|
149
|
+
## Features
|
|
150
|
+
|
|
151
|
+
Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): stdio and Streamable HTTP transports, pluggable auth (`none` / `jwt` / `oauth`), swappable storage (`in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`), structured logging with optional OpenTelemetry tracing.
|
|
119
152
|
|
|
120
153
|
Pixoo-specific:
|
|
121
154
|
|
|
@@ -123,22 +156,18 @@ Pixoo-specific:
|
|
|
123
156
|
- All composition happens in an RGBA canvas pipeline on the host (`@cyanheads/pixoo-toolkit`) — the device receives final RGB frames, never raw drawing commands
|
|
124
157
|
- Styled text engine: gradient palette ramps, drop shadows, outlines, integer scale, semantic alignment — no manual pixel math or bitmap letterforms required
|
|
125
158
|
- Push pacing: device commands serialized with a configurable minimum inter-push interval (default 1000ms) to prevent device freezes
|
|
126
|
-
-
|
|
127
|
-
- Animation capped at 40 frames (device instability beyond this); contact-sheet PNG preview for animations (GIF inconsistent across MCP clients)
|
|
128
|
-
- Local transports only — `sharp` image processing doesn't run on Cloudflare Workers
|
|
159
|
+
- Animation capped at 40 frames (device instability beyond this); contact-sheet PNG preview for animations (GIF preview is inconsistent across MCP clients)
|
|
129
160
|
|
|
130
161
|
Agent-friendly output:
|
|
131
162
|
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
163
|
+
- 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
|
+
- 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 flags visibility issues (screen off, brightness ≤ 10, wrong channel) as enrichment notices rather than failures
|
|
166
|
+
- Graceful degradation — render succeeds and returns the preview even when the device is unreachable, so the agent keeps its work
|
|
136
167
|
|
|
137
168
|
## Getting started
|
|
138
169
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
Add the following to your MCP client configuration file:
|
|
170
|
+
Add the following to your MCP client configuration file. Run `pixoo_discover_devices` to find your Pixoo's IP, then set `PIXOO_IP` below.
|
|
142
171
|
|
|
143
172
|
```json
|
|
144
173
|
{
|
|
@@ -204,7 +233,7 @@ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 PIXOO_IP=192.168.1.50 bun run start:h
|
|
|
204
233
|
|
|
205
234
|
### Prerequisites
|
|
206
235
|
|
|
207
|
-
- [Bun v1.
|
|
236
|
+
- [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
208
237
|
- A Divoom Pixoo LED matrix display on the local network (Pixoo-64, Pixoo-32, or Pixoo-16). Discovery tools and pure-render tools (`push: false`) work without a configured device.
|
|
209
238
|
|
|
210
239
|
### Installation
|
|
@@ -240,13 +269,13 @@ All configuration is validated at startup via Zod schemas in `src/config/server-
|
|
|
240
269
|
|
|
241
270
|
| Variable | Description | Default |
|
|
242
271
|
|:---------|:------------|:--------|
|
|
243
|
-
| `PIXOO_IP` | Device IP address on the local network. Required for device tools (`pixoo_display_text`, `pixoo_compose_scene`, `pixoo_push_image`, `pixoo_overlay_text`, `pixoo_control_device`). Discovery and pure-render (`push: false`) work without it. | — |
|
|
272
|
+
| `PIXOO_IP` | Device IP address on the local network. **Required for device tools** (`pixoo_display_text`, `pixoo_compose_scene`, `pixoo_push_image`, `pixoo_overlay_text`, `pixoo_control_device`). Discovery and pure-render (`push: false`) work without it. | — |
|
|
244
273
|
| `PIXOO_SIZE` | Display size in pixels: `16`, `32`, or `64`. | `64` |
|
|
245
274
|
| `PIXOO_OUTPUT_DIR` | Directory for auto-saving preview PNG and GIF files. When unset, previews are returned in-response only. | — |
|
|
246
275
|
| `PIXOO_PUSH_MIN_INTERVAL_MS` | Minimum interval between device pushes in milliseconds. Prevents device freeze from rapid-fire commands. | `1000` |
|
|
247
276
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
248
277
|
| `MCP_HTTP_PORT` | HTTP server port. | `3010` |
|
|
249
|
-
| `MCP_SESSION_MODE` | HTTP session handling: `stateful`, `stateless`, or `auto
|
|
278
|
+
| `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` |
|
|
250
279
|
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
|
|
251
280
|
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
|
|
252
281
|
| `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
|
|
@@ -290,8 +319,8 @@ The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `
|
|
|
290
319
|
|
|
291
320
|
## Project structure
|
|
292
321
|
|
|
293
|
-
|
|
|
294
|
-
|
|
322
|
+
| Directory | Purpose |
|
|
323
|
+
|:----------|:--------|
|
|
295
324
|
| `src/index.ts` | `createApp()` entry point — registers tools/resources and initializes the Pixoo service. |
|
|
296
325
|
| `src/config/` | Server-specific environment variable parsing and validation with Zod. |
|
|
297
326
|
| `src/mcp-server/tools/` | Tool definitions (`*.tool.ts`). |
|
|
@@ -307,11 +336,11 @@ See [`CLAUDE.md`/`AGENTS.md`](./CLAUDE.md) for development guidelines and archit
|
|
|
307
336
|
- Handlers throw, framework catches — no `try/catch` in tool logic
|
|
308
337
|
- Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage
|
|
309
338
|
- The renderer (`src/renderer/`) is pure — no device dependency, testable without hardware
|
|
310
|
-
- All device calls go through `PixooService`; every `PixooResult` is checked
|
|
339
|
+
- All device calls go through `PixooService`; every `PixooResult` is checked — never assume a push succeeded
|
|
311
340
|
|
|
312
341
|
## Contributing
|
|
313
342
|
|
|
314
|
-
Issues
|
|
343
|
+
Issues are welcome. Run checks and tests before submitting:
|
|
315
344
|
|
|
316
345
|
```sh
|
|
317
346
|
bun run devcheck
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "@cyanheads/mcp-ts-core ^0.13.2 adoption: explicit stateless session mode, a structured -32602 argument-rejection envelope, unset-env normalization for PIXOO_* vars, and the framework skill tree moved to framework-skills/. Claude and Codex plugin manifests now forward PIXOO_IP/PIXOO_SIZE."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 1.1.2 — 2026-09-16
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **Plugin device config** — the Claude plugin declares `userConfig` (`pixoo_ip`/`pixoo_size`) wired to `PIXOO_IP`/`PIXOO_SIZE`; the Codex plugin forwards both via `env_vars`. A Codex plugin install previously had no way to set the device IP at all.
|
|
12
|
+
- **Server card publishes the resolved session mode** under `_meta["io.github.cyanheads.mcp-ts-core/sessionMode"]` ([mcp-ts-core#387](https://github.com/cyanheads/mcp-ts-core/issues/387)).
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
|
|
16
|
+
- **Argument rejections carry `structuredContent.error`** (`code: -32602`, `isError: true`) alongside the existing readable text ([mcp-ts-core#377](https://github.com/cyanheads/mcp-ts-core/issues/377)).
|
|
17
|
+
- **An empty or unsubstituted `${…}` `PIXOO_*` value now reads as unset** rather than as a literal value, through `parseEnvConfig` ([mcp-ts-core#427](https://github.com/cyanheads/mcp-ts-core/issues/427)).
|
|
18
|
+
- **Server identity (name/version) resolves from the served package**, not the working directory ([mcp-ts-core#373](https://github.com/cyanheads/mcp-ts-core/issues/373), [mcp-ts-core#374](https://github.com/cyanheads/mcp-ts-core/issues/374)).
|
|
19
|
+
- Framework skill tree moved `skills/` → `framework-skills/` ([mcp-ts-core#428](https://github.com/cyanheads/mcp-ts-core/issues/428)); scripts and `.github/` templates synced to match.
|
|
20
|
+
- Bun engines floor raised to `>=1.4.0`.
|
|
21
|
+
|
|
22
|
+
## Fixed
|
|
23
|
+
|
|
24
|
+
- **`sessionMode: 'stateless'` is now declared in `src/index.ts`** — an HTTP run with `MCP_SESSION_MODE` unset resolves `stateless` instead of `stateful`; an explicit value still overrides it ([mcp-ts-core#376](https://github.com/cyanheads/mcp-ts-core/issues/376)).
|
|
25
|
+
- **`SIGTERM`/`SIGINT` now end the process explicitly** once shutdown settles, instead of waiting on the event loop to drain ([mcp-ts-core#435](https://github.com/cyanheads/mcp-ts-core/issues/435)).
|
|
26
|
+
|
|
27
|
+
## Dependencies
|
|
28
|
+
|
|
29
|
+
- `@cyanheads/mcp-ts-core` ^0.12.3 → ^0.13.2
|
|
30
|
+
- `zod` ^4.4.3 → ^4.6.4
|
|
31
|
+
- `sharp` ^0.35.3 → ^0.35.4
|
|
32
|
+
- `@biomejs/biome` 2.5.9 → 2.5.13
|
|
33
|
+
- `@types/node` 26.2.0 → 26.5.1
|
|
34
|
+
- `ignore` ^7.0.6 → ^7.0.9
|
|
35
|
+
- `tsc-alias` ^1.9.2 → ^1.9.5
|
|
36
|
+
- `vitest` ^4.1.11 → ^5.0.0
|
|
@@ -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
|
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
|
|
|
@@ -117,30 +117,13 @@ security: false
|
|
|
117
117
|
in that unrelated item's metadata.
|
|
118
118
|
|
|
119
119
|
TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
|
|
120
|
-
via `gh release create --notes-from-tag`.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
Dependency bumps: ← section header
|
|
128
|
-
← blank line
|
|
129
|
-
- `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
|
|
130
|
-
← blank line
|
|
131
|
-
Changed: ← only sections with entries
|
|
132
|
-
← blank line
|
|
133
|
-
- `format()` output includes `query` in text mode
|
|
134
|
-
← blank line
|
|
135
|
-
Added:
|
|
136
|
-
← blank line
|
|
137
|
-
- `manifest.json` scaffolded for MCPB bundle support
|
|
138
|
-
- Install badges (Claude Desktop, Cursor, VS Code)
|
|
139
|
-
← blank line
|
|
140
|
-
<N> tests pass; `bun run devcheck` clean. ← footer
|
|
141
|
-
|
|
142
|
-
Never a flat comma-separated string. Always structured markdown with
|
|
143
|
-
sections. The tag must scan well as a rendered GitHub Release page.
|
|
120
|
+
via `gh release create --notes-from-tag`. It is a condensed digest of this
|
|
121
|
+
entry, never a copy, and its format is owned by the `release-and-publish`
|
|
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.
|
|
144
127
|
-->
|
|
145
128
|
|
|
146
129
|
## Added
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,8 @@ await createApp({
|
|
|
37
37
|
pixooDesignGuideResource,
|
|
38
38
|
],
|
|
39
39
|
prompts: [],
|
|
40
|
+
// No handler requests input mid-call, so nothing needs a 2025-era session.
|
|
41
|
+
sessionMode: 'stateless',
|
|
40
42
|
/**
|
|
41
43
|
* The tool and resource surface is fixed at build time — nothing registers or
|
|
42
44
|
* retires a definition at runtime — so the list results are safe for shared
|
|
@@ -51,8 +53,6 @@ await createApp({
|
|
|
51
53
|
setup(core) {
|
|
52
54
|
initPixooService(core.config, core.storage);
|
|
53
55
|
},
|
|
54
|
-
instructions: '
|
|
55
|
-
'pixoo_display_text is the 80% case for styled text. pixoo_compose_scene for layered scenes, widgets, and animations. ' +
|
|
56
|
-
'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.',
|
|
57
57
|
});
|
|
58
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;;;;;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,30 @@ 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";
|
|
474
475
|
}, {
|
|
475
476
|
readonly reason: "device_rejected";
|
|
476
477
|
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
477
478
|
readonly when: "Device firmware returned a non-zero error code.";
|
|
478
479
|
readonly recovery: "Note the device error code and check the Pixoo documentation.";
|
|
480
|
+
readonly thrownBy: "service";
|
|
479
481
|
}, {
|
|
480
482
|
readonly reason: "no_device_configured";
|
|
481
483
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
482
484
|
readonly when: "PIXOO_IP is not set and push was requested.";
|
|
483
485
|
readonly recovery: "Run pixoo_discover_devices to find the device IP, then set PIXOO_IP.";
|
|
486
|
+
readonly thrownBy: "service";
|
|
484
487
|
}, {
|
|
485
488
|
readonly reason: "asset_not_found";
|
|
486
489
|
readonly code: JsonRpcErrorCode.NotFound;
|
|
487
490
|
readonly when: "An image or sprite path could not be read.";
|
|
488
491
|
readonly recovery: "Verify the file path exists and is readable, or check the URL is reachable.";
|
|
492
|
+
readonly thrownBy: "service";
|
|
489
493
|
}, {
|
|
490
494
|
readonly reason: "invalid_color";
|
|
491
495
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
492
496
|
readonly when: "A color value could not be resolved.";
|
|
493
|
-
readonly recovery: "Use #RRGGBB
|
|
497
|
+
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
498
|
}, {
|
|
495
499
|
readonly reason: "unknown_icon";
|
|
496
500
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwT5B,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,34 @@ 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
374
|
},
|
|
375
375
|
{
|
|
376
376
|
reason: 'device_rejected',
|
|
377
377
|
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
378
378
|
when: 'Device firmware returned a non-zero error code.',
|
|
379
379
|
recovery: 'Note the device error code and check the Pixoo documentation.',
|
|
380
|
+
thrownBy: 'service',
|
|
380
381
|
},
|
|
381
382
|
{
|
|
382
383
|
reason: 'no_device_configured',
|
|
383
384
|
code: JsonRpcErrorCode.InvalidParams,
|
|
384
385
|
when: 'PIXOO_IP is not set and push was requested.',
|
|
385
386
|
recovery: 'Run pixoo_discover_devices to find the device IP, then set PIXOO_IP.',
|
|
387
|
+
thrownBy: 'service',
|
|
386
388
|
},
|
|
387
389
|
{
|
|
388
390
|
reason: 'asset_not_found',
|
|
389
391
|
code: JsonRpcErrorCode.NotFound,
|
|
390
392
|
when: 'An image or sprite path could not be read.',
|
|
391
393
|
recovery: 'Verify the file path exists and is readable, or check the URL is reachable.',
|
|
394
|
+
thrownBy: 'service',
|
|
392
395
|
},
|
|
393
396
|
{
|
|
394
397
|
reason: 'invalid_color',
|
|
395
398
|
code: JsonRpcErrorCode.InvalidParams,
|
|
396
399
|
when: 'A color value could not be resolved.',
|
|
397
|
-
recovery: 'Use #RRGGBB
|
|
400
|
+
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
401
|
},
|
|
399
402
|
{
|
|
400
403
|
reason: 'unknown_icon',
|
|
@@ -430,7 +433,7 @@ export const pixooComposeScene = tool('pixoo_compose_scene', {
|
|
|
430
433
|
// collapse traversal segments, passing everything.
|
|
431
434
|
if (input.output &&
|
|
432
435
|
(!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
|
|
436
|
+
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
437
|
}
|
|
435
438
|
ctx.log.info('Rendering scene', {
|
|
436
439
|
elements: input.elements.length,
|
|
@@ -449,10 +452,10 @@ export const pixooComposeScene = tool('pixoo_compose_scene', {
|
|
|
449
452
|
}
|
|
450
453
|
catch (err) {
|
|
451
454
|
if (err instanceof Error && err.message.includes('Unknown color')) {
|
|
452
|
-
throw ctx.fail('invalid_color', `${err.message}. Valid named colors: ${validColorNames}.
|
|
455
|
+
throw ctx.fail('invalid_color', `${err.message}. Valid named colors: ${validColorNames}.`, ctx.recoveryFor('invalid_color'));
|
|
453
456
|
}
|
|
454
457
|
if (err instanceof McpError && err.data?.['reason'] === 'unknown_icon') {
|
|
455
|
-
throw ctx.fail('unknown_icon', `${err.message} Valid icons: ${validIconNames}.
|
|
458
|
+
throw ctx.fail('unknown_icon', `${err.message} Valid icons: ${validIconNames}.`, ctx.recoveryFor('unknown_icon'));
|
|
456
459
|
}
|
|
457
460
|
throw err;
|
|
458
461
|
}
|