
Pixoo Mcp Server
io.github.cyanheads由 GitHub 帳號 cyanheads 發布v1.5.0更新於 Oct 30, 2026
Render and push styled pixel art, text, dashboards, and animations to Divoom Pixoo LED displays.
概覽
渲染像素畫、樣式文字、儀表板、動畫和 HTML 頁面,並推送到區域網路內的 Divoom Pixoo LED 顯示器。
- 可以做什麼
- 渲染並推送帶主題、漸層、陰影和捲動效果的樣式文字
- 以文字、圖示、形狀、小工具、圖片和精靈合成分層場景,可靜態或動畫
- 縮放並推送本機或遠端圖片、GIF 和 WebP,支援調色盤精簡與抖動
- 在無頭瀏覽器中依面板尺寸逐格渲染 HTML/CSS/SVG/Canvas 頁面
- 設定或清除由裝置渲染的捲動文字疊加層
- 讀取和變更亮度、螢幕狀態、頻道和錶面,並探索區域網路裝置
- 適用情境
- 適合在區域網路內擁有 Divoom Pixoo 16、32 或 64 顯示器,希望由助理設計並展示文字、儀表板、像素畫或動畫,或查看與調整裝置狀態的情境。
- 執行需求
- 透過 npx 在本機執行(需 Node.js 24+ 或 Bun 1.4+)。推送、疊加層和裝置控制需將 PIXOO_IP 設為顯示器的區域網路位址,PIXOO_SIZE 應與面板一致。HTML 渲染需要本機 chrome-headless-shell,可透過 PIXOO_BROWSER_PATH 或 Puppeteer 快取定位。裝置探索需連線 Divoom 雲端端點,因此需要網際網路。超過 40 格的動畫要求裝置能連到本機,需設定 PIXOO_SERVE_HOST 和 PIXOO_SERVE_PORT。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Pixoo Mcp Server,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
@cyanheads/pixoo-mcp-server
Render and push styled pixel art, text, dashboards, and animations to Divoom Pixoo LED displays on your local network via MCP. STDIO or Streamable HTTP.
Overview
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, animations, and HTML pages, or read and change device state.
Tools
Resources
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.
Capability reference
pixoo_display_text tool
textas a string or an array of lines;theme(midnight,ember,claude,ice,neon,forest,mono) sets the background and default palette.styletakes apaletteramp (ember,ice,neon,fire,lavender,claude,mono) or a custom{ from, to }, plusshadow,outline, andscale1–8;positionis semantic or in pixels, andalignlines up multi-line textfont:standard(5×7) andcompact(3×5) draw printable ASCII plus° ← ↑ → ↓ ▲ ▼ ♥ · …, and any other character (€,’, a newline inside one string) as?, named with its code point and line index in the responsenotice;numeralsis an 11×18 digit face for clocks and readouts that draws 0–9, space, and: . - + / % ° ?, and text holding any other character fails validation, naming those characterslayout[]reports every fit decision as anaction(shrunk-to-compactwhen the text fits only in the compact font,scrollingwhen the returned frames scroll,noneotherwise), thefontused, and whether each line's boxfitson the panel; a single line too wide for the panel starts at x 0, cut at the right edge when it does not scroll. Single-line text falls back from the standard to the compact font unlessfontis set — never tonumerals— and text still too wide only scrolls undereffect: "auto"or"scroll"effect:scrollmakes one pass in up to 40 frames,autoscrolls only on overflow, andfloatandpulseloop over 20 frames;framesreports the count, and the animation pushes as one device animation
pixoo_compose_scene tool
- Up to 50
elementsdrawn back-to-front:text(in the same three fonts aspixoo_display_text),icon,rect,circle,line,progress,sparkline,bitmap,pixels,image(absolute path or https URL, with the samefinishaspixoo_push_image),sprite(absolute path). Thebackgroundis a solid color, av/h/rgradient, or atheme - Every element takes
opacity(each pixel lands at its own alpha ×opacity, so soft edges fade evenly) andblend:normal,add(glows and light beams),screen, ormultiply.lineand outlinecircletakestrokeWidthandantialias, and arectborder takesstrokeWidth, growing inward; either field on a shape that draws no stroke fails validation, naming it - Returns
layout[]: each element's placed box — for a wide or anti-aliased stroke, every pixel it draws — and whether itfitson the panel. Elements are placed as given and never refit, soactionis alwaysnone. An absoluteoutputpath saves the first frame as a PNG in place of thePIXOO_OUTPUT_DIRauto-save. Typed failures:asset_not_found,invalid_image(an image or sprite that was read but does not decode),invalid_color,unknown_icon,invalid_output_path - Animation through per-element
effectpresets (float,scroll-left,scroll-right,pulse,blink,twinkle,drift,fade-in,fade-out) or rawanimatekeyframes overdx,dy,opacity(numbers or numeric strings),visible(true/false), andcolor(interpolated through RGB on any element with acolor), each track holding at least one keyframe;frames1–800,speed10–2000 ms per frame (default 150). Past 40 frames the scene plays as one GIF the device downloads from this host, withspeedrounded to 10 ms. An element takeseffectoranimate, not both. An effect'samplitudesets the movement offloat,scroll-*, anddrift, and the 0–1 depth of thepulseandtwinkleopacity dip
pixoo_push_image tool
sourceis an absolute local path or an https URL, with downloads capped at 10 MB;fitiscontain(default),cover, orfill, andkernelisnearest(default, for pixel art),lanczos3(photos), ormitchell- A source that decodes as an animated GIF or WebP, whatever its file name, pushes as an animation of up to
maxFramesframes (1–800, default 40), sampled evenly from a longer source; past 40 frames it plays as one GIF the device downloads from this host. It plays at the source's total duration over the pushed frame count (150 ms when the source records no delays), or atspeed(10–2000 ms per frame);frames,sourceFrames, andspeedreport what was pushed finishreduces the image to a palette before the push: exactly one ofcolors(2–256, built from the image) orpalette(1–256 hex or named colors), plusdither(none,bayer4,floyd-steinberg). Transparent pixels stay unlit, and an animation'scolorspalette is shared by every frame- An unreadable path or URL fails as
asset_not_found, a source that is read but does not decode (a text file, an HTML page, a truncated download) fails asinvalid_image, and an unresolvablefinishpalette entry fails asinvalid_color; the preview is the exact frame the device receives, or a grid of an animation's frames
pixoo_render_html tool
-
htmlis a full document or a body fragment, up to 500,000 characters, laid out in a square viewport one CSS pixel per LED. The page is the panel:bodyhas no margin, scrollbars are hidden, and a page that paints no background renders on black (unlit). Its own CSS overrides each -
frames1–800 (default 1),speed10–2000 ms per frame (default 150). Each frame advances a virtual clock byspeed:window.render(t, frame), when the page defines it, runs before each capture witht = frame / frames, so periodic motion loops seamlessly;requestAnimationFrame,setTimeout/setInterval(4 ms floor for nested and repeating timers),Date(starting at the real time), andperformance.now()(0 at load) follow the same clock, and CSS animations are paused and seeked to it, so two calls capture the same frames unless the page readsMath.random(unseeded) or the startingDate.requestIdleCallbackand iframes keep real time. Past 40 frames the page plays as one GIF the device downloads from this host, as onpixoo_compose_scene -
sampling:native(default) orsupersample, which renders at 8× and area-averages into each LED, smoothing transforms, text, SVG, and canvas. Chromium snaps a plain box's edges to whole CSS pixels before scaling, so a box atleft: 0.5pxstill lands on one LED; move it withtransformfor sub-pixel motion.finishtakes the same palette reduction aspixoo_push_image, applied before the preview -
Nothing loads from the network and workers are blocked.
pageErrorsreturns the page's uncaught errors,console.erroroutput, and blocked URLs (a blocked navigation asBlocked navigation: <url>): the first 20, each cut to 500 characters. Awindow.renderthat throws or rejects fails aspage_error, naming the frame; with no browser found, the call fails asbrowser_unavailablewith install steps (see Local development);render_timeoutandrender_crashedcover a render past 30 s and a crashed browser or page.PIXOO_HTML_ENABLED=falseremoves the tool -
Every page gets a
pixooglobal before its own scripts run, for the crisp bitmap text, palettes, and icons that browser anti-aliasing would smear across LEDs:pixoo.context()is the 2D context of a transparent panel-size canvas fixed over the page.pixoo.text(ctx, text, x, y, { font, color, palette, scale, shadow, outline })drawspixoo_display_text's fonts.pixoo.icon(ctx, name, x, y, { w, h, color, palette })draws a registry icon.pixoo.palettesholds the 7 palettes, andpixoo.sizeis the panel size.
Text and icons drawn this way match
pixoo_compose_scenepixel for pixel in both sampling modes. An unknown palette, icon, or color throws naming it: -
pixoo_design_briefwith topichtmlcovers loops, the clock, sampling, thepixooruntime, and legibility at 64 px. A dot orbiting the panel once per loop:
pixoo_overlay_text tool
mode: "set"or"clear"on one of 20 slots (id0–19).setrequirestextand takes a devicefontID (0–114),x/ywithinPIXOO_SIZE,color,speed(0–100),direction,align, andwidth- Returns
acknowledged,mode, andid. The device renders the overlay, so there is no preview, and it persists across channel switches until cleared
pixoo_control_device tool
- No params reads state; any of
brightness(0–100),screen(on/off),channel(faces/cloud/visualizer/custom), orclockFaceIdis applied before the read-back - Returns
reachable,channel,brightness,screenOn, andclockId(absent when unreachable) plusapplied; a failed setting is left out ofappliedand named in the notice instead of failing the call
pixoo_discover_devices tool
- Queries Divoom's cloud endpoint (
app.divoom-gz.com), so it needs internet access;timeoutMs1000–30000 (default 5000) - Returns each device's
name,id, andip; withPIXOO_IPset,configuredIpFoundsays whether it matched. An unreachable endpoint fails asdiscovery_failed
pixoo_design_brief tool
topic:text,scene,dashboard,animation,pixel-art,html, ortroubleshooting; works without a reachable device- Returns markdown
craftGuidance, a livedeviceContext,htmlRenderer(available,disabledwhenPIXOO_HTML_ENABLED=false, orno_browser, found without launching a browser),nextToolSuggestionsas{ toolName, reason, args }with arguments pre-filled for the topic and device state, plusavailableThemesandiconCategories
pixoo://device/status resource
- Uncached live read:
reachable,channel,brightness,screenOn,clockId,displaySize,configuredIp - Returns
reachable: falserather than an error when the device can't be reached orPIXOO_IPis unset
pixoo://reference/themes resource
- Every theme (background,
textPalette,accent,shadow) and palette (from/tostops), plusthemeNamesandpaletteNamesfor thethemeandpaletteparameters - Static registry, cached for 24h
pixoo://reference/icons resource
- Each icon's
name,category, andviewBox, plus abyCategorygrouping (weather, arrows, status, media); anamegoes in apixoo_compose_sceneicon element - Static registry, cached for 24h
pixoo://reference/design-guide resource
text/markdown: legibility floors, palette discipline, layout zones, animation budget, effect presets, pixel art rules, push pacing, and known device behaviors- The whole guide in one document, where
pixoo_design_briefreturns guidance per topic; cached for 24h
Features
Built on @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.
Pixoo-specific:
- All composition happens on the host in an RGBA canvas pipeline (
@cyanheads/pixoo-toolkit); the device receives finished RGB frames - 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 - Up to 40 animation frames push one request each; more frame pushes make the device unstable. Past 40,
pixoo_compose_scene,pixoo_push_image, andpixoo_render_html(up to 800 frames) serve one GIF from a one-shot listener on this host, which the device downloads and loops, so the device must be able to reach this host.pixoo_display_textstays within 40
Agent-friendly output:
- Preview on every render:
pixoo_display_text,pixoo_compose_scene,pixoo_push_image, andpixoo_render_htmlreturn the frame as an 8× upscaled PNG image block, pushed or not, sopush: falsechecks a design with no device attached. Animations preview as a grid of their frames (every frame while 1× tiles fit the 512 px sheet, an even sample past that), since GIF display varies across MCP clients; the GIF itself is saved toPIXOO_OUTPUT_DIRwhen set — an 8× preview GIF, or past 40 frames the panel-size GIF the device downloads - Layout transparency:
layout[]reports every renderer decision (font fallback, scrolling, and whether each box fits on the panel) so agents can refine a design - Device truth:
pushedreflects the device ACK (past 40 frames, the ACK of the play plus the device's request for the GIF, with the whole file handed to the OS to send; the device does not confirm receipt), and thedeviceStateread 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) - Renders survive failed pushes: on all four render tools, the typed error (
device_unreachable,device_http_error,device_rejected,gif_serve_failed,no_device_configured) carriesoutputFilespointing at the saved preview (thePIXOO_OUTPUT_DIRcopy, or a temp file when that is unset)
Getting started
Add it to Claude Code, with PIXOO_IP set to your Pixoo's LAN address (pixoo_discover_devices finds it if you don't know it):
For other clients, add this to the MCP config file:
pixoo_render_html also needs a local chrome-headless-shell (install steps). For Docker or a source build, see Running the server.
Configuration
See .env.example for the full list of optional overrides.
Running the server
Local development
Requires Bun v1.4.0 or higher (or Node.js v24+) and a Divoom Pixoo on the local network (Pixoo-64, Pixoo-32, or Pixoo-16).
The HTML renderer also needs chrome-headless-shell. npx @puppeteer/browsers install chrome-headless-shell@stable --path ~/.cache/puppeteer installs it where the server looks by default. Installed anywhere else with --path <dir>, set PIXOO_BROWSER_PATH to the executable path the install prints. The server launches it headless, with no DevTools port, only when a render needs it.
-
Clone and install:
-
Build and run:
-
Run checks and tests:
Docker
To run it over stdio from an MCP client:
The device downloads an animation of more than 40 frames from this host, and on Docker's default bridge network the address routed to the device is the container's own, which the device can't reach. Set PIXOO_SERVE_HOST to the Docker host's LAN address and publish a fixed PIXOO_SERVE_PORT, as above; 40 frames or fewer need neither.
The image defaults to HTTP transport, stateless session mode, and logs to /var/log/pixoo-mcp-server, and ships no browser for pixoo_render_html. To build locally, docker build -t pixoo-mcp-server .; OpenTelemetry peers are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Development guide
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - The renderer (
src/renderer/) is pure — no device dependency, testable without hardware - All device calls go through
PixooService; everyPixooResultis checked — never assume a push succeeded
Contributing
Issues are welcome. Run checks and tests before submitting:
License
Apache-2.0 — see LICENSE for details.
來源:README.md,提交 dfd3df8
工具
0版本歷史
8- v1.5.0最新Oct 29, 2026
- v1.4.1Oct 25, 2026
- v1.4.0Sep 27, 2026
- v1.3.0Sep 27, 2026
- v1.2.1Sep 27, 2026
- v1.2.0Sep 24, 2026
- v1.1.3Sep 22, 2026
- v1.1.2Sep 16, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分
