@cyanheads/pixoo-mcp-server 1.2.0 → 1.2.1
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 +6 -6
- package/CLAUDE.md +6 -6
- package/Dockerfile +30 -10
- package/README.md +61 -86
- package/changelog/1.2.x/1.2.1.md +35 -0
- package/dist/mcp-server/tools/definitions/pixoo-compose-scene.tool.d.ts +77 -11
- 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 +44 -18
- package/dist/mcp-server/tools/definitions/pixoo-compose-scene.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pixoo-design-brief.tool.js +1 -1
- package/dist/renderer/icons.d.ts +14 -4
- package/dist/renderer/icons.d.ts.map +1 -1
- package/dist/renderer/icons.js +30 -22
- package/dist/renderer/icons.js.map +1 -1
- package/dist/renderer/keyframes.d.ts +35 -3
- package/dist/renderer/keyframes.d.ts.map +1 -1
- package/dist/renderer/keyframes.js +85 -52
- package/dist/renderer/keyframes.js.map +1 -1
- package/dist/renderer/remote-image.d.ts +4 -0
- package/dist/renderer/remote-image.d.ts.map +1 -1
- package/dist/renderer/remote-image.js +4 -0
- 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 +137 -106
- package/dist/renderer/scene-renderer.js.map +1 -1
- package/package.json +6 -5
- package/server.json +3 -3
package/AGENTS.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** pixoo-mcp-server
|
|
4
|
-
**Version:** 1.2.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 1.2.1
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.9`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
|
-
**MCP SDK:** `@modelcontextprotocol/server` ^2.
|
|
7
|
+
**MCP SDK:** `@modelcontextprotocol/server` ^2.1.0 (protocol revision 2026-07-28 alongside the 2025 era)
|
|
8
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.
|
|
@@ -166,7 +166,7 @@ Handlers receive a unified `ctx` object. Key properties used by this server:
|
|
|
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
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. |
|
|
169
|
-
| `ctx.state` | Tenant-scoped KV — `.get`, `.set(key, value, { ttl? })`, `.delete`, `.getMany`, `.list`. Keys are validated; colons are not legal separators. |
|
|
169
|
+
| `ctx.state` | Tenant-scoped KV — `.get`, `.set(key, value, { ttl? })`, `.delete`, `.getMany`, `.list`. Accepts any JSON-serializable value; reads return its JSON form (a `Date` comes back as an ISO string). Keys are validated; colons are not legal separators. |
|
|
170
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. |
|
|
171
171
|
| `ctx.signal` | `AbortSignal` for cancellation. Pass it to any long-running I/O. |
|
|
172
172
|
| `ctx.requestId` | Unique request ID. |
|
|
@@ -332,7 +332,7 @@ Available skills:
|
|
|
332
332
|
| `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response when `devcheck` flags a transitive advisory; then `bun update <name>`, then `bun dedupe` |
|
|
333
333
|
| `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — re-resolves every ranged dep (the framework pin included) and rewrites the lockfile as `lockfileVersion: 2` |
|
|
334
334
|
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
335
|
-
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, MCPB `user_config` wiring, plugin manifests, README version
|
|
335
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, MCPB `user_config` wiring, plugin manifests, manifest and README version parity, Dockerfile build platform (run by devcheck) |
|
|
336
336
|
| `bun run list-skills` | Print the skill registry |
|
|
337
337
|
| `bun run tree` | Generate directory structure doc |
|
|
338
338
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
@@ -352,7 +352,7 @@ Available skills:
|
|
|
352
352
|
|
|
353
353
|
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies and strips dependency-shipped agent docs and platform-specific native bindings that root-anchored `.mcpbignore` patterns cannot reach. MCPB is stdio-only — HTTP deployments are unaffected.
|
|
354
354
|
|
|
355
|
-
`lint:packaging` verifies that `server.json` and `manifest.json` declare the same env var names, that every `manifest.json` `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.<key>}"` (the host substitutes nothing else), that an optional string option carries `"default": ""`, that no plugin manifest writes an empty `env` value, and
|
|
355
|
+
`lint:packaging` verifies that `server.json` and `manifest.json` declare the same env var names, that every `manifest.json` `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.<key>}"` (the host substitutes nothing else), that an optional string option carries `"default": ""`, that no plugin manifest writes an empty `env` value, that `manifest.json` and the README `Version-` badge carry the `package.json` version, and that the Dockerfile stage running `bun run build` starts `FROM --platform=$BUILDPLATFORM` (a multi-arch build otherwise runs it under QEMU, where Bun aborts).
|
|
356
356
|
|
|
357
357
|
---
|
|
358
358
|
|
package/CLAUDE.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** pixoo-mcp-server
|
|
4
|
-
**Version:** 1.2.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 1.2.1
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.9`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
|
-
**MCP SDK:** `@modelcontextprotocol/server` ^2.
|
|
7
|
+
**MCP SDK:** `@modelcontextprotocol/server` ^2.1.0 (protocol revision 2026-07-28 alongside the 2025 era)
|
|
8
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.
|
|
@@ -166,7 +166,7 @@ Handlers receive a unified `ctx` object. Key properties used by this server:
|
|
|
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
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. |
|
|
169
|
-
| `ctx.state` | Tenant-scoped KV — `.get`, `.set(key, value, { ttl? })`, `.delete`, `.getMany`, `.list`. Keys are validated; colons are not legal separators. |
|
|
169
|
+
| `ctx.state` | Tenant-scoped KV — `.get`, `.set(key, value, { ttl? })`, `.delete`, `.getMany`, `.list`. Accepts any JSON-serializable value; reads return its JSON form (a `Date` comes back as an ISO string). Keys are validated; colons are not legal separators. |
|
|
170
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. |
|
|
171
171
|
| `ctx.signal` | `AbortSignal` for cancellation. Pass it to any long-running I/O. |
|
|
172
172
|
| `ctx.requestId` | Unique request ID. |
|
|
@@ -332,7 +332,7 @@ Available skills:
|
|
|
332
332
|
| `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response when `devcheck` flags a transitive advisory; then `bun update <name>`, then `bun dedupe` |
|
|
333
333
|
| `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — re-resolves every ranged dep (the framework pin included) and rewrites the lockfile as `lockfileVersion: 2` |
|
|
334
334
|
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
335
|
-
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, MCPB `user_config` wiring, plugin manifests, README version
|
|
335
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, MCPB `user_config` wiring, plugin manifests, manifest and README version parity, Dockerfile build platform (run by devcheck) |
|
|
336
336
|
| `bun run list-skills` | Print the skill registry |
|
|
337
337
|
| `bun run tree` | Generate directory structure doc |
|
|
338
338
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
@@ -352,7 +352,7 @@ Available skills:
|
|
|
352
352
|
|
|
353
353
|
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies and strips dependency-shipped agent docs and platform-specific native bindings that root-anchored `.mcpbignore` patterns cannot reach. MCPB is stdio-only — HTTP deployments are unaffected.
|
|
354
354
|
|
|
355
|
-
`lint:packaging` verifies that `server.json` and `manifest.json` declare the same env var names, that every `manifest.json` `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.<key>}"` (the host substitutes nothing else), that an optional string option carries `"default": ""`, that no plugin manifest writes an empty `env` value, and
|
|
355
|
+
`lint:packaging` verifies that `server.json` and `manifest.json` declare the same env var names, that every `manifest.json` `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.<key>}"` (the host substitutes nothing else), that an optional string option carries `"default": ""`, that no plugin manifest writes an empty `env` value, that `manifest.json` and the README `Version-` badge carry the `package.json` version, and that the Dockerfile stage running `bun run build` starts `FROM --platform=$BUILDPLATFORM` (a multi-arch build otherwise runs it under QEMU, where Bun aborts).
|
|
356
356
|
|
|
357
357
|
---
|
|
358
358
|
|
package/Dockerfile
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
# is architecture-independent — emulating this stage buys nothing, and Bun 1.4
|
|
10
10
|
# aborts with MemoryExhaustion under QEMU x86_64 when it is emulated.
|
|
11
11
|
# ==============================================================================
|
|
12
|
-
FROM --platform=$BUILDPLATFORM oven/bun:1.4.
|
|
12
|
+
FROM --platform=$BUILDPLATFORM oven/bun:1.4.2 AS build
|
|
13
13
|
|
|
14
14
|
WORKDIR /usr/src/app
|
|
15
15
|
|
|
@@ -35,7 +35,7 @@ RUN bun run build
|
|
|
35
35
|
# application. It uses a slim base image and only includes production
|
|
36
36
|
# dependencies and build artifacts.
|
|
37
37
|
# ==============================================================================
|
|
38
|
-
FROM oven/bun:1.4.
|
|
38
|
+
FROM oven/bun:1.4.2-slim AS production
|
|
39
39
|
|
|
40
40
|
WORKDIR /usr/src/app
|
|
41
41
|
|
|
@@ -51,8 +51,15 @@ LABEL org.opencontainers.image.licenses="Apache-2.0"
|
|
|
51
51
|
LABEL org.opencontainers.image.version="${APP_VERSION}"
|
|
52
52
|
LABEL org.opencontainers.image.source="https://github.com/cyanheads/pixoo-mcp-server"
|
|
53
53
|
|
|
54
|
-
# Copy dependency manifests
|
|
55
|
-
|
|
54
|
+
# Copy dependency manifests. `bunfig.toml` rides along so every install below
|
|
55
|
+
# passes its release-age gate and security scanner, as a local install does.
|
|
56
|
+
COPY package.json bun.lock bunfig.toml ./
|
|
57
|
+
|
|
58
|
+
# The scanner bunfig.toml names is a devDependency, and Bun installs a missing
|
|
59
|
+
# scanner through the same production-filtered install, which omits it and
|
|
60
|
+
# aborts. Seed it from the build stage's full install instead. Remove this line
|
|
61
|
+
# together with the scanner if bunfig.toml stops naming one.
|
|
62
|
+
COPY --from=build /usr/src/app/node_modules/@socketsecurity/bun-security-scanner ./node_modules/@socketsecurity/bun-security-scanner
|
|
56
63
|
|
|
57
64
|
# Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
|
|
58
65
|
# that are not needed in the final production image.
|
|
@@ -65,21 +72,35 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
|
65
72
|
bun install --production --omit=peer --frozen-lockfile --ignore-scripts
|
|
66
73
|
|
|
67
74
|
# Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
|
|
68
|
-
#
|
|
69
|
-
# with: docker build --build-arg OTEL_ENABLED=
|
|
75
|
+
# Installed by default. Omit them for a leaner image at build time
|
|
76
|
+
# with: docker build --build-arg OTEL_ENABLED=false
|
|
77
|
+
# Each package is requested at the range the installed framework declares in
|
|
78
|
+
# `peerDependencies`, so the resolution stays inside the framework's tested
|
|
79
|
+
# peer range; a name with no declared range fails the build.
|
|
70
80
|
ARG OTEL_ENABLED=true
|
|
71
81
|
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
72
82
|
if [ "$OTEL_ENABLED" = "true" ]; then \
|
|
73
|
-
bun
|
|
74
|
-
|
|
83
|
+
specs=$(bun -e ' \
|
|
84
|
+
const { peerDependencies: peers } = await Bun.file("node_modules/@cyanheads/mcp-ts-core/package.json").json(); \
|
|
85
|
+
const names = process.argv.slice(1); \
|
|
86
|
+
const missing = names.filter((name) => !peers?.[name]); \
|
|
87
|
+
if (missing.length > 0) throw new Error(`no peerDependencies range for ${missing.join(", ")}`); \
|
|
88
|
+
console.log(names.map((name) => `${name}@${peers[name]}`).join(" ")); \
|
|
89
|
+
' \
|
|
90
|
+
@hono/otel \
|
|
91
|
+
@opentelemetry/api-logs \
|
|
92
|
+
@opentelemetry/exporter-logs-otlp-http \
|
|
75
93
|
@opentelemetry/exporter-metrics-otlp-http \
|
|
76
94
|
@opentelemetry/exporter-trace-otlp-http \
|
|
95
|
+
@opentelemetry/instrumentation-http \
|
|
77
96
|
@opentelemetry/instrumentation-pino \
|
|
78
97
|
@opentelemetry/resources \
|
|
98
|
+
@opentelemetry/sdk-logs \
|
|
79
99
|
@opentelemetry/sdk-metrics \
|
|
80
100
|
@opentelemetry/sdk-node \
|
|
81
101
|
@opentelemetry/sdk-trace-node \
|
|
82
|
-
@opentelemetry/semantic-conventions
|
|
102
|
+
@opentelemetry/semantic-conventions) \
|
|
103
|
+
&& bun add --omit=dev --omit=peer --ignore-scripts $specs; \
|
|
83
104
|
fi
|
|
84
105
|
|
|
85
106
|
# Copy the compiled application code from the build stage
|
|
@@ -123,7 +144,6 @@ ENV MCP_TRANSPORT_TYPE="http"
|
|
|
123
144
|
ENV MCP_SESSION_MODE="stateless"
|
|
124
145
|
ENV MCP_LOG_LEVEL="info"
|
|
125
146
|
ENV LOGS_DIR="/var/log/pixoo-mcp-server"
|
|
126
|
-
ENV MCP_FORCE_CONSOLE_LOGGING="true"
|
|
127
147
|
|
|
128
148
|
# Expose the port the server listens on
|
|
129
149
|
EXPOSE ${MCP_HTTP_PORT}
|
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
|
|
|
@@ -23,130 +23,109 @@
|
|
|
23
23
|
|
|
24
24
|
## Overview
|
|
25
25
|
|
|
26
|
-
Divoom Pixoo LED matrix displays
|
|
26
|
+
Divoom Pixoo LED matrix displays on the local network, with the Pixoo-64 as the primary target and the 16 and 32 also supported. Render and push styled text, layered scenes, dashboards, and animations, or read and change device state. Runs as a stdio process or a local Streamable HTTP server.
|
|
27
27
|
|
|
28
28
|
### Tools
|
|
29
29
|
|
|
30
30
|
| Tool | Description |
|
|
31
|
-
|
|
32
|
-
| `pixoo_display_text` | Render styled text
|
|
33
|
-
| `pixoo_compose_scene` | Compose
|
|
34
|
-
| `pixoo_push_image` |
|
|
35
|
-
| `pixoo_overlay_text` | Set or clear a device-
|
|
36
|
-
| `pixoo_control_device` | Read or change
|
|
37
|
-
| `pixoo_discover_devices` | Find Pixoo devices
|
|
38
|
-
| `pixoo_design_brief` |
|
|
31
|
+
|:---|:---|
|
|
32
|
+
| `pixoo_display_text` | Render styled text with themes, gradients, shadows, and auto-fit, static or animated, and push it |
|
|
33
|
+
| `pixoo_compose_scene` | Compose layered scenes of text, icons, widgets, shapes, bitmaps, images, and sprites, static or animated |
|
|
34
|
+
| `pixoo_push_image` | Resize a local or https image to the LED grid and push it |
|
|
35
|
+
| `pixoo_overlay_text` | Set or clear a device-rendered scrolling text overlay |
|
|
36
|
+
| `pixoo_control_device` | Read or change brightness, screen state, channel, or clock face |
|
|
37
|
+
| `pixoo_discover_devices` | Find Pixoo devices and their LAN IPs through Divoom's cloud discovery |
|
|
38
|
+
| `pixoo_design_brief` | Craft guidance for a design topic, with live device state and pre-filled next calls |
|
|
39
39
|
|
|
40
40
|
### Resources
|
|
41
41
|
|
|
42
42
|
| Resource | Description |
|
|
43
43
|
|:---|:---|
|
|
44
|
-
| `pixoo://device/status` | Live snapshot
|
|
45
|
-
| `pixoo://reference/themes` | Theme and palette registry
|
|
46
|
-
| `pixoo://reference/icons` | Built-in icon names
|
|
47
|
-
| `pixoo://reference/design-guide` | Long-form
|
|
44
|
+
| `pixoo://device/status` | Live device snapshot: reachability, channel, brightness, screen state, display size |
|
|
45
|
+
| `pixoo://reference/themes` | Theme and palette registry |
|
|
46
|
+
| `pixoo://reference/icons` | Built-in icon names by category |
|
|
47
|
+
| `pixoo://reference/design-guide` | Long-form craft guide for the 64px display |
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Tools cover the same ground for tool-only clients: `pixoo_control_device` reads the live device state, and `pixoo_design_brief` returns craft guidance, theme names, and icon names.
|
|
50
50
|
|
|
51
51
|
## Capability reference
|
|
52
52
|
|
|
53
53
|
### `pixoo_display_text` <sub>tool</sub>
|
|
54
54
|
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
- Auto-fit overflow tries standard font, then compact, then marks the text `scrolling`; an explicit `font` is used as given, so text too wide for it scrolls in that font instead of shrinking; every fit decision is reported in `layout[]` with an `action` (`shrunk-to-compact`, `scrolling`, `wrapped`, `truncated`, `clipped`)
|
|
59
|
-
- `effect` animates the text: `scroll` runs it across the display once (up to 40 frames), `auto` scrolls only text too wide to fit, `float` and `pulse` loop over 20 frames; animations push as one device animation and report `frames`
|
|
60
|
-
- Optional `brightness` (0–100) applied before push — a failure is a warning via an enrichment notice, not a tool error
|
|
61
|
-
- Returns the rendered frame (or a grid of every animation frame) as an image content block; `outputFiles` (PNG, or GIF when animated) is populated only when `PIXOO_OUTPUT_DIR` is configured
|
|
55
|
+
- `text` as a string or an array of lines; `theme` (`midnight`, `ember`, `claude`, `ice`, `neon`, `forest`, `mono`) sets the background and default palette. `style` takes a `palette` ramp (`ember`, `ice`, `neon`, `fire`, `lavender`, `claude`, `mono`) or a custom `{ from, to }`, plus `shadow`, `outline`, and `scale` 1–8; `position` is semantic or in pixels, and `align` lines up multi-line text
|
|
56
|
+
- `layout[]` reports every fit decision as an `action` (`shrunk-to-compact`, `scrolling`, `truncated`, …). Single-line text falls back from the standard to the compact font unless `font` is set, and text still too wide only scrolls under `effect: "auto"` or `"scroll"`
|
|
57
|
+
- `effect`: `scroll` makes one pass in up to 40 frames, `auto` scrolls only on overflow, and `float` and `pulse` loop over 20 frames; `frames` reports the count, and the animation pushes as one device animation
|
|
62
58
|
|
|
63
59
|
---
|
|
64
60
|
|
|
65
61
|
### `pixoo_compose_scene` <sub>tool</sub>
|
|
66
62
|
|
|
67
|
-
- Up to 50
|
|
68
|
-
-
|
|
69
|
-
- Animation
|
|
70
|
-
- `image` elements accept an absolute local path or an https URL and fit the configured display size; `sprite` elements take an absolute local path; a supplied `output` path must be absolute with no traversal segments, and replaces the `PIXOO_OUTPUT_DIR` auto-save for that call
|
|
71
|
-
- `opacity` (0–100) blends any element, images included, over the layers beneath it
|
|
72
|
-
- Static scenes return a PNG preview; animations return a contact-sheet PNG tiling every frame, plus a GIF saved to `PIXOO_OUTPUT_DIR` when configured (GIF preview is inconsistent across MCP clients)
|
|
73
|
-
- Typed failures for `asset_not_found`, `invalid_color`, and `unknown_icon`, alongside the shared device-error reasons
|
|
63
|
+
- Up to 50 `elements` drawn back-to-front: `text`, `icon`, `rect`, `circle`, `line`, `progress`, `sparkline`, `bitmap`, `pixels`, `image` (absolute path or https URL), `sprite` (absolute path). The `background` is a solid color, a `v` / `h` / `r` gradient, or a `theme`
|
|
64
|
+
- Returns `layout[]`: each element's placed box and whether it `fits` on the panel. An absolute `output` path saves the first frame as a PNG in place of the `PIXOO_OUTPUT_DIR` auto-save. Typed failures: `asset_not_found`, `invalid_color`, `unknown_icon`, `invalid_output_path`
|
|
65
|
+
- Animation through per-element `effect` presets (`float`, `scroll-left`, `scroll-right`, `pulse`, `blink`, `twinkle`, `drift`, `fade-in`, `fade-out`) or raw `animate` keyframes over `dx`, `dy`, `opacity` (numbers or numeric strings), `visible` (`true`/`false`), and `color` (interpolated through RGB on any element with a `color`), each track holding at least one keyframe; `frames` 1–40, `speed` 10–2000 ms per frame (default 150). An effect's `amplitude` sets the movement of `float`, `scroll-*`, and `drift`, and the 0–1 depth of the `pulse` and `twinkle` opacity dip
|
|
74
66
|
|
|
75
67
|
---
|
|
76
68
|
|
|
77
69
|
### `pixoo_push_image` <sub>tool</sub>
|
|
78
70
|
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
- Three resize kernels: `nearest` for pixel art (default), `lanczos3` for photos, `mitchell` for a balance
|
|
82
|
-
- Returns the exact resized result as an image content block before it is pushed
|
|
71
|
+
- `source` is an absolute local path or an https URL, with downloads capped at 10 MB; `fit` is `contain` (default), `cover`, or `fill`, and `kernel` is `nearest` (default, for pixel art), `lanczos3` (photos), or `mitchell`
|
|
72
|
+
- An unreadable path or URL fails as `asset_not_found`; the preview is the exact resized frame the device receives
|
|
83
73
|
|
|
84
74
|
---
|
|
85
75
|
|
|
86
76
|
### `pixoo_overlay_text` <sub>tool</sub>
|
|
87
77
|
|
|
88
|
-
- `mode: "set"`
|
|
89
|
-
-
|
|
90
|
-
- Configurable `x`/`y` (0 to display size − 1, checked against `PIXOO_SIZE`), 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
|
|
91
|
-
- Device-rendered, not previewable — for styled, previewable text use `pixoo_display_text`
|
|
78
|
+
- `mode: "set"` or `"clear"` on one of 20 slots (`id` 0–19). `set` requires `text` and takes a device `font` ID (0–114), `x` / `y` within `PIXOO_SIZE`, `color`, `speed` (0–100), `direction`, `align`, and `width`
|
|
79
|
+
- Returns `acknowledged`, `mode`, and `id`. The device renders the overlay, so there is no preview, and it persists across channel switches until cleared
|
|
92
80
|
|
|
93
81
|
---
|
|
94
82
|
|
|
95
83
|
### `pixoo_control_device` <sub>tool</sub>
|
|
96
84
|
|
|
97
|
-
-
|
|
98
|
-
- `
|
|
99
|
-
- Always returns current `reachable`, `channel`, `brightness`, `screenOn`, and `clockId` (the latter three absent when the device is unreachable)
|
|
85
|
+
- No params reads state; any of `brightness` (0–100), `screen` (`on` / `off`), `channel` (`faces` / `cloud` / `visualizer` / `custom`), or `clockFaceId` is applied before the read-back
|
|
86
|
+
- Returns `reachable`, `channel`, `brightness`, `screenOn`, and `clockId` (absent when unreachable) plus `applied`; a failed setting is left out of `applied` and named in the notice instead of failing the call
|
|
100
87
|
|
|
101
88
|
---
|
|
102
89
|
|
|
103
90
|
### `pixoo_discover_devices` <sub>tool</sub>
|
|
104
91
|
|
|
105
|
-
- Queries Divoom's cloud
|
|
106
|
-
- Returns each device's name
|
|
107
|
-
- When `PIXOO_IP` is already configured, flags whether it matches a discovered device (`configuredIpFound`) and notes a mismatch
|
|
108
|
-
- `timeoutMs` configurable 1000–30000ms (default 5000ms)
|
|
92
|
+
- Queries Divoom's cloud endpoint (`app.divoom-gz.com`), so it needs internet access; `timeoutMs` 1000–30000 (default 5000)
|
|
93
|
+
- Returns each device's `name`, `id`, and `ip`; with `PIXOO_IP` set, `configuredIpFound` says whether it matched. An unreachable endpoint fails as `discovery_failed`
|
|
109
94
|
|
|
110
95
|
---
|
|
111
96
|
|
|
112
97
|
### `pixoo_design_brief` <sub>tool</sub>
|
|
113
98
|
|
|
114
|
-
-
|
|
115
|
-
- Returns markdown
|
|
116
|
-
- `nextToolSuggestions` entries are `{ toolName, reason, args }`, with `args` pre-filled with ready-to-use arguments (`{}` when the tool needs none) tailored to the topic and current device state (e.g. suggests `pixoo_discover_devices` when the device is unreachable)
|
|
117
|
-
- Also returns `availableThemes` and `iconCategories` for direct use in other tools
|
|
99
|
+
- `topic`: `text`, `scene`, `dashboard`, `animation`, `pixel-art`, or `troubleshooting`; works without a reachable device
|
|
100
|
+
- Returns markdown `craftGuidance`, a live `deviceContext`, `nextToolSuggestions` as `{ toolName, reason, args }` with arguments pre-filled for the topic and device state, plus `availableThemes` and `iconCategories`
|
|
118
101
|
|
|
119
102
|
---
|
|
120
103
|
|
|
121
104
|
### `pixoo://device/status` <sub>resource</sub>
|
|
122
105
|
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
- Equivalent to calling `pixoo_control_device` with no params
|
|
106
|
+
- Uncached live read: `reachable`, `channel`, `brightness`, `screenOn`, `clockId`, `displaySize`, `configuredIp`
|
|
107
|
+
- Returns `reachable: false` rather than an error when the device can't be reached or `PIXOO_IP` is unset
|
|
126
108
|
|
|
127
109
|
---
|
|
128
110
|
|
|
129
111
|
### `pixoo://reference/themes` <sub>resource</sub>
|
|
130
112
|
|
|
131
|
-
- Every
|
|
132
|
-
-
|
|
133
|
-
- Compile-time constants — cached for 24h
|
|
113
|
+
- Every theme (background, `textPalette`, `accent`, `shadow`) and palette (`from` / `to` stops), plus `themeNames` and `paletteNames` for the `theme` and `palette` parameters
|
|
114
|
+
- Static registry, cached for 24h
|
|
134
115
|
|
|
135
116
|
---
|
|
136
117
|
|
|
137
118
|
### `pixoo://reference/icons` <sub>resource</sub>
|
|
138
119
|
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
- Compile-time constants — cached for 24h
|
|
120
|
+
- Each icon's `name`, `category`, and `viewBox`, plus a `byCategory` grouping (weather, arrows, status, media); a `name` goes in a `pixoo_compose_scene` icon element
|
|
121
|
+
- Static registry, cached for 24h
|
|
142
122
|
|
|
143
123
|
---
|
|
144
124
|
|
|
145
125
|
### `pixoo://reference/design-guide` <sub>resource</sub>
|
|
146
126
|
|
|
147
|
-
-
|
|
148
|
-
- `
|
|
149
|
-
- Same content `pixoo_design_brief` surfaces per topic — this resource is the complete reference in one document
|
|
127
|
+
- `text/markdown`: legibility floors, palette discipline, layout zones, animation budget, effect presets, pixel art rules, push pacing, and known device behaviors
|
|
128
|
+
- The whole guide in one document, where `pixoo_design_brief` returns guidance per topic; cached for 24h
|
|
150
129
|
|
|
151
130
|
## Features
|
|
152
131
|
|
|
@@ -154,22 +133,20 @@ Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): s
|
|
|
154
133
|
|
|
155
134
|
Pixoo-specific:
|
|
156
135
|
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
-
|
|
160
|
-
- Push pacing: device commands serialized with a configurable minimum inter-push interval (default 1000ms) to prevent device freezes
|
|
161
|
-
- Animation capped at 40 frames (device instability beyond this); contact-sheet PNG preview for animations (GIF preview is inconsistent across MCP clients)
|
|
136
|
+
- All composition happens on the host in an RGBA canvas pipeline (`@cyanheads/pixoo-toolkit`); the device receives finished RGB frames
|
|
137
|
+
- Pushes switch the device to the custom channel and run one at a time, spaced by `PIXOO_PUSH_MIN_INTERVAL_MS` (default 1000) so rapid pushes don't freeze the device
|
|
138
|
+
- Animations cap at 40 frames, past which the device becomes unstable
|
|
162
139
|
|
|
163
140
|
Agent-friendly output:
|
|
164
141
|
|
|
165
|
-
- Preview
|
|
166
|
-
- Layout transparency
|
|
167
|
-
- Device truth
|
|
168
|
-
-
|
|
142
|
+
- Preview on every render: `pixoo_display_text`, `pixoo_compose_scene`, and `pixoo_push_image` return the frame as an 8× upscaled PNG image block, pushed or not, so `push: false` checks a design with no device attached. Animations preview as a grid of every frame, since GIF display varies across MCP clients; the GIF itself is saved to `PIXOO_OUTPUT_DIR` when set
|
|
143
|
+
- Layout transparency: `layout[]` reports every renderer decision (font fallback, truncation, scrolling, clipping) so agents can refine a design
|
|
144
|
+
- Device truth: `pushed` reflects the device ACK, and the `deviceState` read back after a push comes with a notice naming the fix when the render won't be visible (screen off, brightness ≤ 10, off the custom channel)
|
|
145
|
+
- Renders survive failed pushes: the typed error (`device_unreachable`, `device_http_error`, `device_rejected`, `no_device_configured`) carries `outputFiles` pointing at the saved preview (the `PIXOO_OUTPUT_DIR` copy, or a temp file when that is unset)
|
|
169
146
|
|
|
170
147
|
## Getting started
|
|
171
148
|
|
|
172
|
-
Add the following to your MCP client configuration file
|
|
149
|
+
Add the following to your MCP client configuration file, with `PIXOO_IP` set to your Pixoo's LAN address. `pixoo_discover_devices` finds it if you don't know it.
|
|
173
150
|
|
|
174
151
|
```json
|
|
175
152
|
{
|
|
@@ -236,7 +213,7 @@ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 PIXOO_IP=192.168.1.50 bun run start:h
|
|
|
236
213
|
### Prerequisites
|
|
237
214
|
|
|
238
215
|
- [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
239
|
-
- A Divoom Pixoo
|
|
216
|
+
- A Divoom Pixoo on the local network (Pixoo-64, Pixoo-32, or Pixoo-16).
|
|
240
217
|
|
|
241
218
|
### Installation
|
|
242
219
|
|
|
@@ -267,22 +244,20 @@ cp .env.example .env
|
|
|
267
244
|
|
|
268
245
|
## Configuration
|
|
269
246
|
|
|
270
|
-
All configuration is validated at startup via Zod schemas in `src/config/server-config.ts`. Key environment variables:
|
|
271
|
-
|
|
272
247
|
| Variable | Description | Default |
|
|
273
|
-
|
|
274
|
-
| `PIXOO_IP` | Device IP
|
|
248
|
+
|:---|:---|:---|
|
|
249
|
+
| `PIXOO_IP` | Device IP on the local network. **Required** for pushes, overlays, and device control; discovery, design briefs, and `push: false` renders work without it. | — |
|
|
275
250
|
| `PIXOO_SIZE` | Display size in pixels: `16`, `32`, or `64`. | `64` |
|
|
276
|
-
| `PIXOO_OUTPUT_DIR` | Directory
|
|
277
|
-
| `PIXOO_PUSH_MIN_INTERVAL_MS` | Minimum
|
|
251
|
+
| `PIXOO_OUTPUT_DIR` | Directory where render tools save preview PNG and GIF files. Unset, previews are returned only in the response. | — |
|
|
252
|
+
| `PIXOO_PUSH_MIN_INTERVAL_MS` | Minimum gap between device pushes, in ms. | `1000` |
|
|
278
253
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
279
254
|
| `MCP_HTTP_PORT` | HTTP server port. | `3010` |
|
|
280
|
-
| `MCP_SESSION_MODE` | HTTP session
|
|
255
|
+
| `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`. A value set here overrides the server's declared `stateless`. | `stateless` |
|
|
281
256
|
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
|
|
282
257
|
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
|
|
283
258
|
| `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
|
|
284
|
-
| `
|
|
285
|
-
| `OTEL_ENABLED` | Enable OpenTelemetry
|
|
259
|
+
| `LOG_TOOL_FAILURE_PAYLOADS` | Log each failed tool call's arguments and result, redacted by key name and capped at `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` (default `16384`). A secret inside a free-form value is not redacted. | `false` |
|
|
260
|
+
| `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
|
|
286
261
|
|
|
287
262
|
See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
288
263
|
|
|
@@ -317,18 +292,18 @@ docker build -t pixoo-mcp-server .
|
|
|
317
292
|
docker run --rm -e PIXOO_IP=192.168.1.50 -p 3010:3010 pixoo-mcp-server
|
|
318
293
|
```
|
|
319
294
|
|
|
320
|
-
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/pixoo-mcp-server`. OpenTelemetry peer dependencies are installed by default
|
|
295
|
+
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/pixoo-mcp-server`. OpenTelemetry peer dependencies are installed by default; build with `--build-arg OTEL_ENABLED=false` to omit them.
|
|
321
296
|
|
|
322
297
|
## Project structure
|
|
323
298
|
|
|
324
299
|
| Directory | Purpose |
|
|
325
|
-
|
|
326
|
-
| `src/index.ts` | `createApp()` entry point
|
|
300
|
+
|:---|:---|
|
|
301
|
+
| `src/index.ts` | `createApp()` entry point: registers tools and resources and initializes the Pixoo service. |
|
|
327
302
|
| `src/config/` | Server-specific environment variable parsing and validation with Zod. |
|
|
328
|
-
| `src/mcp-server/tools/` | Tool definitions (`*.tool.ts`). |
|
|
303
|
+
| `src/mcp-server/tools/` | Tool definitions (`*.tool.ts`) and the shared post-render push path. |
|
|
329
304
|
| `src/mcp-server/resources/` | Resource definitions (`*.resource.ts`). |
|
|
330
|
-
| `src/services/pixoo/` | `PixooService
|
|
331
|
-
| `src/renderer/` | Pure rendering pipeline: element renderers, styled-text engine, themes, icons, effect compiler, preview encoding
|
|
305
|
+
| `src/services/pixoo/` | `PixooService`: wraps `@cyanheads/pixoo-toolkit` with push pacing, result mapping, and device state reads. |
|
|
306
|
+
| `src/renderer/` | Pure rendering pipeline with no device dependency: element renderers, styled-text engine, themes, icons, effect compiler, preview encoding, remote image fetch. |
|
|
332
307
|
| `tests/` | Unit and integration tests mirroring `src/`. |
|
|
333
308
|
|
|
334
309
|
## Development guide
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "pixoo_compose_scene checks animate keyframes per property and caps image, icon, and sprite sizes; stroke icons, icon palettes, keyframed colors, and layout fits now render and report as documented. mcp-ts-core ^0.13.6 → ^0.13.9."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 1.2.1 — 2026-09-26
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **`animate` keyframe values are checked per property** — `dx`, `dy`, and `opacity` take numbers or numeric strings, `visible` takes `true`/`false` only (numbers are now rejected), and a bad value fails `-32602` naming the keyframe ([#47](https://github.com/cyanheads/pixoo-mcp-server/issues/47)). An empty track fails the same way ([#45](https://github.com/cyanheads/pixoo-mcp-server/issues/45)).
|
|
12
|
+
- **Element sizes are capped** — `image` and `icon` `w`/`h` must be 1–256, and sprite `cols`, `rows`, and `scale` at most 64; out-of-range values fail `-32602` naming the field ([#33](https://github.com/cyanheads/pixoo-mcp-server/issues/33), [#37](https://github.com/cyanheads/pixoo-mcp-server/issues/37), [#38](https://github.com/cyanheads/pixoo-mcp-server/issues/38)).
|
|
13
|
+
- **Registry icons draw their stroke parts as 1-pixel lines** — `snow` and `wind` render, `sun`, `rain`, and the arrows keep their rays, drops, and shafts, and the status icons draw as a ring plus mark instead of a solid disk ([#39](https://github.com/cyanheads/pixoo-mcp-server/issues/39)).
|
|
14
|
+
- **`layout[].fits` is computed from the placed box on all four edges** for every element; `image` and `line` boxes include `dx`/`dy`, `circle` and `line` boxes their last pixel, a `bitmap` box spans its widest row, and a `pixels` box bounds its points ([#42](https://github.com/cyanheads/pixoo-mcp-server/issues/42)).
|
|
15
|
+
- **Effect `amplitude` sets the depth of the `pulse` and `twinkle` opacity dip** on a 0–1 scale; the defaults (0.5, 0.6) keep the 50–100 and 40–100 ranges ([#41](https://github.com/cyanheads/pixoo-mcp-server/issues/41)).
|
|
16
|
+
- **A keyframed `color` interpolates and drives every element with a `color` field** — in-between frames resolve as `#rrggbb` instead of failing `invalid_color`, and `text`, `icon`, `rect`, `circle`, `line`, and `sparkline` animate it, not only `pixels` ([#36](https://github.com/cyanheads/pixoo-mcp-server/issues/36)).
|
|
17
|
+
- **Framework argument handling (mcp-ts-core 0.13.7–0.13.9)** — an integer sent to a string field, or a JSON-stringified object, is repaired before validation; rejections name the field path; stack traces and request context no longer reach client error data.
|
|
18
|
+
- **Opt-in `LOG_TOOL_FAILURE_PAYLOADS` and `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`** log failed calls' arguments and results and export logs over OTLP; the Docker image installs the log-export peers.
|
|
19
|
+
- Repo hygiene: framework skills and scripts resynced with mcp-ts-core 0.13.9; the npm tarball excludes `dist/*.mcpb`; `.mcpbignore` excludes `docs/idea.md` and `logs/`.
|
|
20
|
+
|
|
21
|
+
## Fixed
|
|
22
|
+
|
|
23
|
+
- **`Object.prototype` names such as `constructor` or `__proto__` as an icon `name` fail as `unknown_icon`** instead of an internal `-32603` ([#32](https://github.com/cyanheads/pixoo-mcp-server/issues/32)). The same names as color values are not covered yet.
|
|
24
|
+
- **Icon `palette` paints a top-to-bottom ramp** from the palette's `from` to its `to` color and takes precedence over `color`; it was accepted and ignored ([#35](https://github.com/cyanheads/pixoo-mcp-server/issues/35)).
|
|
25
|
+
- **A `color` keyframe that isn't a color fails as `invalid_color` wherever it sits in the track**, including past the scene's last frame ([#40](https://github.com/cyanheads/pixoo-mcp-server/issues/40)).
|
|
26
|
+
- **Numeric-string keyframes on `dx`, `dy`, and `opacity` interpolate like the numbers they spell** (`"000"` → `"100"` ramps 0 → 100); only the `color` track lerps through RGB ([#43](https://github.com/cyanheads/pixoo-mcp-server/issues/43)).
|
|
27
|
+
- **A line-mode `sparkline` stays inside its `w × h` box** instead of inking one column and one row past it ([#44](https://github.com/cyanheads/pixoo-mcp-server/issues/44)).
|
|
28
|
+
- **The `music` icon's beam joins its two stems** ([#46](https://github.com/cyanheads/pixoo-mcp-server/issues/46)).
|
|
29
|
+
|
|
30
|
+
## Dependencies
|
|
31
|
+
|
|
32
|
+
- `@cyanheads/mcp-ts-core` ^0.13.6 → ^0.13.9 (`@modelcontextprotocol/server` 2.0.0 → 2.1.0, transitive)
|
|
33
|
+
- `@socketsecurity/bun-security-scanner` ^1.1.2 → ^1.1.3 (dev)
|
|
34
|
+
- `ignore` ^7.0.9 → ^7.0.10 (dev)
|
|
35
|
+
- Bun 1.4.0 → 1.4.2 (`packageManager` and Docker base images)
|