pixmith-mcp
v0.1.5
Published
Model Context Protocol server for the Pixmith pixel-art editor: lets AI agents create, edit, inspect and export sprites
Maintainers
Readme
pixmith-mcp
A Model Context Protocol server that gives AI agents the whole
Pixmith pixel-art editor: create and open sprites, draw, select and
transform, manage layers, frames, cels and tags, edit palettes and color modes, run FX filters, build tilemaps with
terrain auto-tiling, tween and bake animations, export PNG / GIF / APNG / sprite sheets / Godot 4 resources, undo and
redo, and run Lua scripts. render returns real images, so the agent can look at what it made.
It is one self-contained Rust binary. The Pixmith engine (pixmith-core)
runs in-process, through the same pixmith_* entry points the macOS and desktop apps use, so documents behave exactly
like in the app: every edit is an undo step, linked cels stay in sync, panics inside the core are caught and the
document is rewound instead of crashing the server.
When the Pixmith app is open on the same computer, the tools edit its documents instead: every edit appears in the window as it happens and is a normal undo step there (see Live mode). With no app running, everything works headless, exactly as described below.
Works with every MCP client that speaks stdio (Claude Code, Claude Desktop, Codex CLI, Google Antigravity, Cursor,
Gemini CLI, VS Code / GitHub Copilot, Windsurf, Cline, Zed, ...). Streamable HTTP is available with --http.
Install
Nothing to install if you have Node.js (16 or newer): every client can start the server with npx, which downloads
the prebuilt binary for your platform (macOS arm64 / x64, Linux x64 / arm64, Windows x64) once and caches it.
{ "command": "npx", "args": ["-y", "pixmith-mcp"] }Add "--root", "/Users/me/art" and the other options below to args after "pixmith-mcp". The npm package
pixmith-mcp only holds a small launcher; the binary comes from @pixmith/mcp-<platform>-<arch>.
Or build it from source, with a Rust toolchain (1.99 or newer) and a C compiler (for the embedded Lua):
cargo install --git https://github.com/azdharsyahputra/pixmith-mcp
# or, from a checkout:
cargo build --release # -> target/release/pixmith-mcpPrebuilt archives are also attached to every GitHub release.
With a binary on disk (not through npx), print the configuration for your client with its absolute path filled in:
pixmith-mcp print-config claude-code # also: claude-desktop codex antigravity cursor gemini vscode windsurf json
pixmith-mcp print-config cursor --root ~/art
pixmith-mcp print-config cursor --live off # never use the Pixmith app (default: auto)print-config only prints; it never edits a client's configuration.
Client setup
Claude Code, Claude Desktop and Cursor below start the server with npx (see Install); with a binary
built from source, use its path instead of npx -y pixmith-mcp. The other examples show a binary path: replace
/path/to/pixmith-mcp with the real one (which pixmith-mcp, or use print-config), or use "command": "npx" with
"-y", "pixmith-mcp" first in args. The optional
--root <dir> limits file access to one folder (recommended, see Security); --allow-dir <dir> (repeatable) additionally allows reading files and writing export output in another folder, such as a temp dir; --live auto|on|off controls
live mode.
Claude Code
claude mcp add pixmith -- npx -y pixmith-mcp --root ~/art
# --scope user: every project; --scope project: shared through .mcp.json in the repositoryClaude Desktop
Settings > Developer > Edit Config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS,
%APPDATA%\Claude\claude_desktop_config.json on Windows), then restart Claude Desktop:
{
"mcpServers": {
"pixmith": { "command": "npx", "args": ["-y", "pixmith-mcp", "--root", "/Users/me/art"] }
}
}Codex CLI
~/.codex/config.toml (or codex mcp add pixmith -- /path/to/pixmith-mcp --root /Users/me/art):
[mcp_servers.pixmith]
command = "/path/to/pixmith-mcp"
args = ["--root", "/Users/me/art"]Google Antigravity
Agent panel > ... > MCP Servers > Manage MCP Servers > View raw config. The file is
~/.gemini/config/mcp_config.json (all workspaces) or <workspace>/.agents/mcp_config.json:
{
"mcpServers": {
"pixmith": { "command": "/path/to/pixmith-mcp", "args": ["--root", "/Users/me/art"] }
}
}Cursor
.cursor/mcp.json in the project, or ~/.cursor/mcp.json for every project:
{
"mcpServers": {
"pixmith": { "command": "npx", "args": ["-y", "pixmith-mcp", "--root", "/Users/me/art"] }
}
}Gemini CLI
~/.gemini/settings.json (or .gemini/settings.json in a project), or gemini mcp add pixmith /path/to/pixmith-mcp --root /Users/me/art:
{
"mcpServers": {
"pixmith": { "command": "/path/to/pixmith-mcp", "args": ["--root", "/Users/me/art"] }
}
}VS Code (GitHub Copilot agent mode)
.vscode/mcp.json in the workspace, or "MCP: Open User Configuration". VS Code uses servers, not mcpServers:
{
"servers": {
"pixmith": { "type": "stdio", "command": "/path/to/pixmith-mcp", "args": ["--root", "${workspaceFolder}"] }
}
}Windsurf
Cascade > MCP servers > Configure (~/.codeium/windsurf/mcp_config.json, %USERPROFILE%\.codeium\windsurf\mcp_config.json on Windows):
{
"mcpServers": {
"pixmith": { "command": "/path/to/pixmith-mcp", "args": ["--root", "/Users/me/art"] }
}
}Anything else
Most other clients (Cline, Roo Code, Zed, LM Studio, ...) take the same mcpServers JSON (print-config json). For
HTTP-only clients run pixmith-mcp --http 127.0.0.1:8765 and point them at http://127.0.0.1:8765/mcp.
What an agent gets
57 tools in 17 groups (pixmith-mcp list-tools prints them; help topic tools gives the agent the same list).
Tools with many related operations take an action argument, which keeps the tool count well below the limits some
clients impose (Cursor, VS Code, Windsurf) while every schema stays flat and explicit.
| Group | Tools | Covers |
| --- | --- | --- |
| Documents | doc_new, doc_open, doc_save, doc_close, doc_list, doc_info, doc_set_active | New sprites (size, background, color mode, palette preset); open .pxmt / .aseprite / .ase; import PNG, animated GIF, grid sprite sheets and sheets with Aseprite JSON; save / save as / save a copy; full inspection (layer tree, frames and durations, cel occupancy, tags, palette, slices, tilesets, user data, selection, history); doc_close closes one document or, with all_headless: true, every headless one |
| App (live mode) | app_status, app_connect, app_disconnect, game_screenshot | Is the Pixmith app connected, which documents it has open; switch between the app and headless editing at runtime; screenshot of the running Godot game (Live Link game view, needs Pixmith 0.5.1+) |
| Viewing | render, frame_diff, get_pixels, get_pixel | PNG image results (auto scale, frames, single layer or group, region, background, onion skin, View > Frame Difference with diff_prev); frame_diff counts the pixels a frame changed, appeared or disappeared against the previous frame (stuck / torn walk parts), per frame or for the whole animation; exact colors as a compact character grid with legend or as hex |
| Drawing | draw_pixels, draw_shape, draw_polygon, draw_curve, draw_stroke, fill, spray, retouch, gradient, draw_text, brush | Batch pixels, Blur and Jumble brushes (retouch), line / rectangle / ellipse (filled or outline), polygons, Bezier curves, freehand strokes with pixel-perfect mode, flood fill (contiguous or global, tolerance, dither patterns), spray, linear / radial gradients with Bayer dither, the built-in pixel font; brush size and shape, ink modes (simple, alpha, lock alpha, behind, shading with a ramp), mirror symmetry with a movable axis, custom brushes |
| Selection | select, iso_grid, clipboard | Rectangle, polygon / lasso, magic wand, select by color, isometric tile (iso_tile, on the grid of the isometric tilemap layer or the one kept per document with iso_grid), all / none / invert, expand / contract / border, replace / add / subtract; delete, move and crop; copy / cut / paste (also a PNG file) as a floating selection, move, commit or cancel |
| Transform | transform, free_transform, warp, sprite_size | Edit > Warp: a blur-free puppet / mesh warp of a cel (handle grid, pins, selection-aware, preview); flips, 90 / 180-degree turns, free rotation with RotSprite (per cel, all frames, all layers); a free-transform session (position, size, angle, skew, anchor); scale sprite; canvas size with anchor or offset |
| Layers & frames | layer, frame, cel, tag | Add / group / duplicate / remove / reorder / into and out of groups / merge down / properties (name, visibility, opacity, 19 blend modes) / PNG as a new layer or, with replace: true, as the new pixels of an existing layer in one undo step; import_sheet cuts a sprite sheet into consecutive frames of a layer; frames add / duplicate / remove / move / durations; cels link / unlink / opacity / copy / paste / move / clear; tags with direction, repeat and color |
| Color | palette, color_mode | Get, set, add, insert, remove, move, duplicate, replace, sort, ramps, presets (PICO-8, DB16/32, Endesga 32, Game Boy, NES, grays), palette from sprite, .gpl / .hex files; RGBA / grayscale / indexed with dithering |
| Filters | filter | Replace color, invert, desaturate, HSL, brightness/contrast, color balance, curves, outline, drop shadow, remove isolated pixels, blur, sharpen, FX > Clean Edges (halo, despeckle, holes, thin outline) and FX > Remove Background (with shadow); per cel / layer / frame, clipped to the selection or a region; previews as images |
| Cleanup | lint, trace | FX > Lint Sprite: a read-only per-cel report (light edge, holes, specks, no outline, strays, semi-transparent, off palette) with capped coordinates; File > Import > Trace Pixel Art: pseudo-pixel art from AI tools onto a real pixel grid (grid pitch and phase, palette, background and shadow removal, outline closing, crop) on an open cel, or straight from a file with doc_open format: "trace_grid" |
| Tilemaps | tileset, tilemap, terrain, terrain_paint | Isometric tilemaps (2:1 diamond cells such as 96 x 48 at a pixel offset, tall tiles with pivots, per-layer offsets for other elevations, terrain painting, Godot ISOMETRIC TileSet export); tilesets (view as image, read and write tile pixels, add / duplicate / delete / rename); tilemap layers (create, convert from image, stamp with flips, read the grid); terrain auto-tiling: sides / blob / corners (dual grid) modes, several terrains with priorities, templates, masks, peers, symmetry, weights, terrain brush / rectangle / flood / overrides, rebuild, missing-tile report |
| Slices & user data | slice, user_data | Slices with 9-patch centers and pivots, keyed per frame; text and color user data of the sprite, layers, cels, tags and slices |
| Tweening | tween | Keys for x, y, rotation, scale, opacity; eases including Bezier; pivots; RotSprite and snapping options; copy / paste keys; layer parents (the tween rig: set_parent, children, composed_transform; thigh -> shin -> boot); bake into cels, rig-aware |
| Export | export, export_godot, export_godot_tileset | PNG (frame, scale), GIF, APNG, PNG sequences, sprite sheets (horizontal / vertical / grid, padding, border, extrude, trim, Aseprite JSON) per tag or per layer; Godot 4 SpriteFrames .tres + atlas (+ .tscn, and Live Link: the running game updates after every edit), Godot 4 TileSet .tres with terrains and collisions; dry runs and previews |
| History | undo, redo, history, history_goto, transaction | Every mutating call is one labeled undo step; jump anywhere in the history; group many calls into one step |
| Scripting | run_lua | The Lua 5.4 scripting API of Pixmith, Aseprite-compatible (Color, Image, Sprite, app.command, …; one undo step, errors roll back) for anything else; sources up to 16 MiB, timeout_seconds 1-120 (default 10). Files (io, os, app.fs, Image{fromFile}, app.open, saveAs) only inside allow_paths (folders inside --root); documents the script creates come back as new headless documents in new_documents |
| Help | help | The usage guide, the Lua API reference, example scripts, palette presets, the tool list, and a short how-to per tool group (documents, layers, drawing, selection, transform, filters, cleanup, tilemaps, slices, tween, export, history, live) |
The server also provides:
- Instructions sent at initialization: the conventions (0-based coordinates, layer 0 at the bottom, colors, undo) and the main workflows.
- Prompts:
create_sprite_animation,export_for_godot,autotile_tileset,review_sprite. - Resources:
pixmith://docs/guide,pixmith://docs/lua-api,pixmith://docs/lua-examples,pixmith://docs/tools, and for every open documentpixmith://documents/<id>/info(JSON) andpixmith://documents/<id>/render.png.
Conventions
- Documents are addressed by the id
doc_new/doc_openreturn ("d1", or"app:doc-3"for a document open in the Pixmith app);doccan be left out when only one document is open (with the app connected: only while the app has one document open). - Pixel coordinates are 0-based from the top-left. Layers are 0-based with layer 0 at the bottom; layers, tags, slices and tilesets can also be given by name. Frames are 0-based. (Lua scripts use 1-based layers and frames, as documented in the Lua API.)
- Tools without
layer/frameuse the document's active layer and frame, which follow newly added layers and frames, like the editor. - Colors are
"#RRGGBB"or"#RRGGBBAA"(also#RGB,rgb(), CSS basic names,transparent). Results always print#rrggbbaa. - Errors come back as tool results with
isError: trueand the core's message; a failed call changes nothing. Unknown argument names and unknown actions are reported with the valid ones, which catches typos early. - A few common synonyms are accepted:
deletefor theremoveaction oflayer,frame,tag,slice,paletteandterrain(andclearofcel:remove/delete),delete_keyfortweenremove_key,remove_tilefortilesetdelete_tile, andscript/codefor thesourceargument ofrun_lua. The tool schemas list the canonical names only. - Headless documents (ids
d1, ...) stay until closed.doc_close {"all_headless": true}closes them all at once (documents with unsaved changes are kept and listed unlessdiscard_changesis true). Past 32 open headless documents,doc_new/doc_openclose the oldest unmodified ones (saved or never edited, never the new one) and say so in the result'snote. layerwithaction: "import_png"adds the PNG as a new layer; withreplace: trueit puts the PNG into the existinglayeratframeinstead (one undo step; the layer keeps name, order, opacity and blend mode;x/yshift it,namerenames the layer).- Memory: the real limit is roughly frames x layers x canvas size (width x height x 4 bytes per cel) for cels that are
not linked. Linking cels (
cellink, orframeduplicatewithlink: true, which makes any number of copies in one undo step) or using a smaller canvas avoids it. In live mode the whole document goes to the app in one message of at most 64 MiB, so a very large document is refused before it is sent (nothing changes in the app). - Large results are bounded:
get_pixelsreturns at most 16384 pixels per call (4096 as hex),renderat most 16 images of at most 4096 px,historyis paginated,doc_infotruncates very long frame lists.
Live mode: edit the open Pixmith app
Start Pixmith 0.5.0 or later (macOS app, or the Windows / Linux / macOS desktop app) and use the tools as usual: the agent's
edits show up in the open window while it works, each call is one undo step named AI: <tool> (for example
AI: draw_shape rectangle) that you can take back with Cmd/Ctrl+Z, and the document is marked as edited. Without an app
running, doc_new / doc_open start it (in the background, once per session; PIXMITH_LAUNCH=0 turns that off)
and wait up to 20 seconds for it. When it cannot be used (not installed, or after app_disconnect) the very same tools
work headless on files, as before; an app that
disappears in the middle of a session is reported as an error instead of being silently replaced by headless editing
(see "No silent fallback" below). No configuration is needed.
agent --MCP--> pixmith-mcp --127.0.0.1, token--> Pixmith app (your open documents)How it works, in short: the app writes a small discovery file (<app data>/Live/<pid>.json) while it runs and listens
on a loopback port. For each editing call the server fetches the document as .pxmt bytes, runs the unchanged tool
code on its copy with the same pixmith-core the app uses, and sends the result back as one undo step. The protocol is
specified in the Pixmith repository (docs/LIVE_PROTOCOL.md). A revision number per document stops the server from
overwriting something you changed in the meantime: it fetches again and redoes the edit on your version (up to 3
times); while a dialog is open the app asks it to wait and it retries for a few seconds.
- Which document? Documents open in the app have the ids
app:<id>(app_statusordoc_listshows them). Passdocon every call: with several documents open in the app a call without it fails (the error names the tab you are looking at) instead of editing whatever tab happens to be in front. With a single documentdocmay be left out.doc_newanddoc_opencreate the sprite in a new tab of the app; pass"target": "headless"to keep a document inside the server only (its id isd1, ...).doc_save,doc_closeanddoc_set_activeact on the app's tab (doc_savesaves with the app, so its dirty marker is cleared;copy: truewrites a copy without touching the tab). - Layer and frame. Tools without
layer/frameuse the app's current layer and frame. A tool aimed at another layer or frame, or one that adds a layer, moves the app's current layer / frame there, so you see where it works. - No silent fallback. Once this server has been connected to the app in a session, a call that creates or edits a
document never quietly continues headless when the app becomes unreachable (it quit, crashed, or dropped the
socket): the server reconnects once, and if that fails the call returns an error ("the Pixmith app was connected but
is not reachable now (...); start it again, or pass
target: "headless"/ callapp_disconnectto work headless on purpose"). Explicit headless work keeps working:target: "headless"ondoc_new/doc_open, a headless id (d1) asdoc, orapp_disconnect. Results of headless creations say"target": "headless"once an app was involved, and so do calls withoutdocthat land on a headless document while the app is connected. A server that was never connected behaves as before (headless, no extra fields). - One call at a time. Every tool call runs on the single engine thread, so parallel tool calls from a client
(several
doc_newat once, say) queue up and their requests to the app never interleave. - Modes.
--live auto(default) uses the app when one is running and falls back to headless otherwise (until it has been connected once, see above);--live onrequires the app (tools fail with a clear message when none is running;target: "headless"still works);--live offnever talks to an app. ThePIXMITH_LIVEenvironment variable (auto|on|off) is the same switch; the flag wins. At runtime,app_statusshows the state,app_disconnectmakes the server work headless untilapp_connect(which also picks an instance withpidwhen several apps run, newest by default). - Several clients, several apps. The newest running instance wins. Discovery files of dead processes are ignored
(and deleted); a file whose port refuses the connection is ignored.
PIXMITH_SUPPORT_DIRreplaces the platform's app data folder here exactly as it does in the apps (the folder isPixmithinside it,pixmithon Linux). - Transactions.
transactionbegin / end works on app documents: the calls inside it edit the server's copy (renderandget_pixelssee them) and the outermostendsends the result as one undo step AI: <label>. If you change the document in the app meanwhile, the transaction is cancelled and nothing is applied;doc_save,undoandredowait forend. - Limits.
historyandhistory_gotoare not available on app documents: the app owns the undo history and every call (or transaction) is its own step.undo/redoare handed to the app when it supportsdocuments.undo/documents.redo; otherwise the tool says to press Cmd/Ctrl+Z in the app. The app's own selection is not visible to the tools; a selection made with theselecttool is kept by the server for the next calls and dropped as soon as the document changes in the app. Each call is a round trip and an undo step, so prefer batch tools (draw_pixels,draw_polygon,run_lua) over thousands of tiny calls.
Godot Live Link and the game view
Needs Pixmith 0.5.1 or later. With the app connected, a Godot game can show the sprite you are drawing, live:
- Export once with
export_godotandsave_settings: true(or Export > Godot SpriteFrames in the app). The document then knows itsres://resource, so the app can tell the game which SpriteFrames it feeds. - Run a game that has the Live Link addon (
godot/addons/pixmith_live_linkin the Pixmith repository, copied into the project'saddonsfolder and enabled) from Godot as a debug build. After every edit, including each tool call of the agent, the game swaps in the new atlas and regions in place: no re-export, no restart. Released builds never do this. game_screenshotreturns a screenshot of the running game as an image, so the agent can edit a sprite and then look at it in the game. Arguments:timeout_ms(default 3000, at most 10000) andscale(integer 1-8, nearest neighbour, long side capped at 2048 px) to enlarge small pixel games. It needs live mode; without the app, or without a running game that has the addon, it fails with a tool error that says so.
export_godot {"project_root": "~/games/demo", "tres_path": "art/slime.tres", "save_settings": true} // once
draw_pixels {"points": [[13, 17], [18, 17]], "color": "#ff004d"} // the running game shows it right away
game_screenshot {"scale": 2} // -> image of the gameTroubleshooting live mode
app_statussaysconnected: falseand gives the reason. Is the app running? In the app, Preferences > Allow AI agents to control Pixmith (Live mode) must be on (it is by default); turning it off closes the port and removes the discovery file. While an agent is connected, the app shows an "AI connected" chip in the title row; its menu lists the client and has Disconnect.- The app was started after the agent: nothing to do in
automode, the next call finds it; or callapp_connect. - "the Pixmith app was connected but is not reachable now": the app quit or dropped the connection mid-session. Start
it again (the next call reconnects), or deliberately work headless with
app_disconnectortarget: "headless".app_statusshows the reason; before this check, such calls silently created headless documents. - "refused the connection" / "protocol": the app and
pixmith-mcpare from incompatible versions; update the older one. - Running the server in a different account, container or sandbox than the app: the discovery folder and the loopback
port must be reachable; set
PIXMITH_SUPPORT_DIRto the app's folder if it is relocated. game_screenshotsays no game answered: start the game from Godot as a debug build with the addon enabled, and make sure the Pixmith app is 0.5.1 or later; a game that is not running or paused in the debugger sends no frame.- You do not want the agent to touch the app: start the server with
--live off(print-config <client> --live offwrites it into the client configuration). - "Broken pipe" / "closed the connection" on a heavy edit: the app closes a connection whose message is over 64 MiB, and
every edit sends the whole document, which grows with frames x layers x canvas size for unlinked cels (hundreds of
frame duplicatecalls, one per frame, got there). The server now refuses such a request before sending, with the size and what to do. Make one call withframe duplicatecount: N(andlink: true), link cels, or use a smaller canvas. - "busy" errors mean a dialog is open in the app or you are in the middle of a stroke; the server already waited a few seconds. Close the dialog and ask again.
Examples
What a session looks like (tool calls as an agent makes them):
doc_new {"width": 32, "height": 32, "palette_preset": "PICO-8"} // -> {"doc": "d1", ...}
layer {"action": "add", "name": "body"}
draw_shape {"shape": "ellipse", "from": [8, 10], "to": [23, 27], "color": "#00e436", "filled": true}
draw_shape {"shape": "ellipse", "from": [8, 10], "to": [23, 27], "color": "#1d2b53"}
draw_pixels {"points": [[13, 17], [18, 17]], "color": "#1d2b53"}
render {} // -> PNG image
frame {"action": "add", "count": 3}
tween {"action": "set_keys", "frame": 0, "values": {"y": 0}}
tween {"action": "set_keys", "frame": 3, "values": {"y": -3}, "ease": "out_quad"}
tween {"action": "bake"} // optional: keys -> editable cels
tag {"action": "add", "name": "bounce", "from": 0, "to": 3, "direction": "ping_pong"}
doc_save {"path": "slime.pxmt"}
export {"format": "gif", "path": "slime.gif", "scale": 4}
export_godot {"project_root": "~/games/demo", "tres_path": "art/slime.tres", "tscn": true}Or just ask: "Make a 32x32 slime with a 4-frame bounce, export it as a GIF and into my Godot project at
~/games/demo", and the agent picks the tools (the create_sprite_animation prompt spells out a good plan).
Pixmith documents (.pxmt)
doc_open and doc_save go through the core's own pixmith_open / pixmith_save: a path ending in .pxmt is
saved in Pixmith's native format (full sprite plus an embedded thumbnail, see the
format spec); any other path is saved as
.aseprite. Opening detects the format from the file's content. Tools for Pixmith project folders (project_*) will
follow.
Security
- The server reads and writes files only when a tool asks for it (open, save, export, palette files, PNG import,
Lua files). Start it with
--root <dir>to confine all of that to one folder: every path is resolved (symbolic links included) and rejected when it leaves the root; relative paths start at the root. Missing folders inside the root are created on save / export. --allow-dir <dir>(repeatable, needs--rootto matter) opens extra folders for reading and for the output ofexport;doc_saveand other writes stay inside the root. Arun_luaallow_pathsentry inside such a folder lets that script write there too.- Without
--root, the agent can read and overwrite any file your user account can. Clients usually ask before a tool runs; the tool annotations (readOnlyHint,destructiveHint,idempotentHint) let them tell harmless calls from writes. - Lua scripts run in Pixmith's sandbox: no
io,os,requireor bytecode, with instruction, time and memory limits (10 s by default,timeout_secondsraises it up to 120 s and the instruction budget with it; source up to 16 MiB). --httpbinds where you tell it (use127.0.0.1), accepts only localHostheaders and local browser origins, and has no authentication. Prefer stdio.- The server never touches Pixmith's own settings, autosave or script folders. In live mode it only reads the
discovery files in the app's
Livefolder and talks to the app on127.0.0.1with the per-launch token from that file (readable by your user only);--live offdisables all of it. File paths given todoc_open/doc_savefor app documents go through--rootlike every other path.
Development
cargo test # unit tests + the binary driven over stdio for every tool group
cargo fmt --all -- --check && cargo clippy --all-targets -- -D warnings
npx @modelcontextprotocol/inspector --cli target/debug/pixmith-mcp --method tools/listReleases: bump version in Cargo.toml and push the tag vX.Y.Z. .github/workflows/release.yml builds every
platform, attaches the archives to a GitHub release and publishes the npm packages that npm/build.mjs stages (run the
workflow by hand for a dry run). It needs the NPM_TOKEN repository secret, an npm automation token allowed to publish
pixmith-mcp and the @pixmith scope.
pixmith-core comes from the Pixmith repository, pinned to a commit in Cargo.toml; uncomment the [patch] section
there to build against a local checkout. See CONTRIBUTING.md.
License
MIT, see LICENSE. docs/LUA_API.md is copied from the Pixmith repository (same license and author).
