voidless-mcp 1.6.7 → 1.6.9

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.
Files changed (3) hide show
  1. package/README.md +14 -1
  2. package/package.json +9 -1
  3. package/tools.js +59 -8
package/README.md CHANGED
@@ -53,6 +53,18 @@ Claude Code:
53
53
  claude mcp add voidless -e VOIDLESS_API_KEY=void_... -- npx -y voidless-mcp
54
54
  ```
55
55
 
56
+ ### Claude Code plugin
57
+
58
+ The hosted connector plus skills for character sets, tilesets and Godot import
59
+ ([source](https://github.com/voidless-03/voidless-mcp)):
60
+
61
+ ```
62
+ /plugin marketplace add voidless-03/voidless-mcp
63
+ /plugin install voidless@voidless
64
+ ```
65
+
66
+ Or just the hosted connector, no key: `claude mcp add --transport http voidless https://mcp.voidless.dev/mcp`
67
+
56
68
  ## Tools
57
69
 
58
70
  **Your library** (free, read-only)
@@ -79,7 +91,8 @@ claude mcp add voidless -e VOIDLESS_API_KEY=void_... -- npx -y voidless-mcp
79
91
  | `rotate_sprite` | An 8-way set (front, back, sides, diagonals) in one pass |
80
92
  | `extract_parts` | Split a character into body parts for rigging |
81
93
  | `upload_image` | Add an image you already have to your library |
82
- | `pixelate_image` | Convert any image to pixel art |
94
+ | `pixelate_image` | Convert any image to pixel art (32 to 1024 px) |
95
+ | `clean_up_sprite` | Make a sprite or a 1-5 s animation from anywhere game-ready: background removed, pixel art on a real grid, stray pixels fixed; videos come back as 16 frames |
83
96
 
84
97
  **Animation**
85
98
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "voidless-mcp",
3
3
  "mcpName": "dev.voidless/voidless",
4
- "version": "1.6.7",
4
+ "version": "1.6.9",
5
5
  "description": "MCP server for Voidless: generate game sprites, animations, sound, music, video and 3D models",
6
6
  "license": "MIT",
7
7
  "author": "Voidless Dev Inc. <contact@voidless.dev>",
@@ -20,6 +20,14 @@
20
20
  "node": ">=18"
21
21
  },
22
22
  "homepage": "https://voidless.dev",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/voidless-03/voidless-mcp.git",
26
+ "directory": "server"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/voidless-03/voidless-mcp/issues"
30
+ },
23
31
  "keywords": [
24
32
  "mcp",
25
33
  "model-context-protocol",
package/tools.js CHANGED
@@ -25,7 +25,7 @@ import { Agent, setGlobalDispatcher } from "undici";
25
25
  // server does.
26
26
  setGlobalDispatcher(new Agent({ headersTimeout: 0, bodyTimeout: 0, connectTimeout: 30_000 }));
27
27
 
28
- export const VERSION = "1.6.7";
28
+ export const VERSION = "1.6.9";
29
29
  const BASE_URL = process.env.VOIDLESS_API_URL || "https://api.voidless.dev";
30
30
  const CDN_URL = "https://cdn.voidless.dev";
31
31
  // Where asset links point. Results link to the asset's page in the app rather than to raw GIFs: the
@@ -46,7 +46,7 @@ const GALLERY_MIME = "text/html;profile=mcp-app";
46
46
  const GALLERY_TOOLS = [
47
47
  "generate_image", "upload_image", "edit_image", "rotate_sprite", "animate_sprite", "generate_animation_set",
48
48
  "flip_left_side", "generate_video", "generate_sfx", "generate_music", "generate_3d_model", "get_asset",
49
- "list_assets", "extract_parts", "pixelate_image", "extend_video",
49
+ "list_assets", "extract_parts", "pixelate_image", "extend_video", "clean_up_sprite",
50
50
  ];
51
51
  const GALLERY_HTML = readFileSync(new URL("./gallery.html", import.meta.url), "utf8");
52
52
 
@@ -620,9 +620,9 @@ export function createVoidlessServer(API_KEY) {
620
620
  ),
621
621
  dimension: z.enum(["2d", "top down", "isometric"]).default("2d").describe("'2d' = side view, 'top down' = overhead, 'isometric' = 2.5D"),
622
622
  size: z
623
- .union([z.literal(32), z.literal(64), z.literal(128), z.literal(512), z.literal(1024)])
623
+ .union([z.literal(32), z.literal(64), z.literal(128), z.literal(256), z.literal(512), z.literal(1024)])
624
624
  .default(512)
625
- .describe("Output px. 32/64/128 give a pixel-art look; 512 for every other style; 1024 for high-res."),
625
+ .describe("Output px. 32/64/128/256 give a pixel-art look; 512 for every other style; 1024 for high-res."),
626
626
  colours: z.number().int().min(4).max(12).default(8).describe("Colour count for pixel-art reduction (4–12)"),
627
627
  style: z
628
628
  .string()
@@ -736,7 +736,7 @@ export function createVoidlessServer(API_KEY) {
736
736
  server.tool(
737
737
  "upload_image",
738
738
  "Add an image the user already has to their library, so it can be animated, rotated or edited. It must " +
739
- "be square at 32, 64, 128, 512 or 1024 px. Free.",
739
+ "be square at 32, 64, 128, 256, 512 or 1024 px. Free.",
740
740
  {
741
741
  image: z.string().describe("Base64 image (a data: prefix is stripped automatically)"),
742
742
  type: z.enum(["object", "character", "character portrait", "artwork"]).default("character"),
@@ -755,7 +755,8 @@ export function createVoidlessServer(API_KEY) {
755
755
  "saved to the library. 1 credit.",
756
756
  {
757
757
  image: z.string().describe("Base64 source image (a data: prefix is stripped automatically)"),
758
- size: z.union([z.literal(32), z.literal(64), z.literal(128), z.literal(512)]).default(64),
758
+ size: z.union([z.literal(32), z.literal(64), z.literal(128), z.literal(256), z.literal(512), z.literal(1024)]).default(64)
759
+ .describe("Pixel size. 512 and 1024 are drawn on a real 4x grid (128 / 256 art pixels)"),
759
760
  colours: z.number().int().min(4).max(12).default(8),
760
761
  },
761
762
  writeTool("Pixelate image"),
@@ -771,6 +772,56 @@ export function createVoidlessServer(API_KEY) {
771
772
  })
772
773
  );
773
774
 
775
+ server.tool(
776
+ "clean_up_sprite",
777
+ "Make a sprite or a short animation made ANYWHERE (another AI tool, an artist, a screen recording) " +
778
+ "game-ready with Voidless's own clean-up: background removed with stray pieces checked (sparks and dust " +
779
+ "kept, smudges removed); pixel art put on a real pixel grid with one palette and stray pixels fixed. A 1-5 s " +
780
+ "video comes back as 16 frames: kind 'loop' cuts the cleanest loop (walk, run, idle), 'once' keeps the " +
781
+ "whole move (attack, jump). Saved to the user's library. Give the file as a public URL (best for videos) or " +
782
+ "base64. 1 free run a day (10 on Voidless+), then 1 credit an image, 2 a video. For a quick pixel-art " +
783
+ "version of an already clean image, pixelate_image is enough.",
784
+ {
785
+ url: z.string().optional().describe("Public http(s) link to the image or video. Use this for videos"),
786
+ file: z.string().optional().describe("Base64 file instead of url (a data: prefix is stripped automatically)"),
787
+ kind: z.enum(["image", "video"]).optional().describe("What the file is. Inferred from the url's extension when omitted"),
788
+ style: z.enum(["pixel", "smooth"]).default("pixel").describe("pixel = pixel art at 32-1024; smooth = clean cut-out at 512 or 1024"),
789
+ size: z.union([z.literal(32), z.literal(64), z.literal(128), z.literal(256), z.literal(512), z.literal(1024)]).optional()
790
+ .describe("Output size. Default 64 for pixel, 512 for smooth (smooth allows only 512 or 1024)"),
791
+ motion: z.enum(["loop", "once"]).default("loop").describe("Video only: loop = find the cleanest loop; once = keep the whole clip"),
792
+ },
793
+ writeTool("Clean up sprite"),
794
+ tool(async ({ url, file, kind, style, size, motion }) => {
795
+ if (!url && !file) throw new Error("Give the file as a url or as base64 in file.");
796
+ const pixel = style !== "smooth";
797
+ const out = size || (pixel ? 64 : 512);
798
+ if (!pixel && out !== 512 && out !== 1024) throw new Error("Smooth art comes back at 512 or 1024.");
799
+ const guessed = /\.(mp4|webm|mov|m4v)(\?|$)/i.test(String(url || "")) ? "video"
800
+ : /^data:video\//i.test(String(file || "")) ? "video" : "image";
801
+ const body = { kind: kind || guessed, size: out, pixel, loop: motion };
802
+ if (url) body.url = url;
803
+ else body.data = String(file).replace(/^data:[^,]*,/, "");
804
+ const data = await apiPost("/cleanup/run", body);
805
+ const link = data.assetId ? `${APP_URL}/asset/${data.assetId}` : undefined;
806
+ const lines = [
807
+ `Cleaned up: ${data.kind === "video" ? `${(data.images || []).length} frames` : "1 sprite"} at ${data.size}px${data.pixel ? " pixel art" : ""}.`,
808
+ data.assetId ? `Asset ID: ${data.assetId} · ${link}` : null,
809
+ data.kind === "video" && Array.isArray(data.span) ? `Frames taken from ${data.span[0]}s to ${data.span[1]}s of the clip.` : null,
810
+ data.free ? `Free run (${data.freeLeft} free left today).` : null,
811
+ money(data),
812
+ ].filter(Boolean);
813
+ const content = [{ type: "text", text: lines.join("\n") }];
814
+ if (data.kind === "video") {
815
+ const frame = { id: 0, name: "Clean-up", type: "animation", images: data.images || [], gif: data.gif };
816
+ content.push(...(await frameContent([frame])));
817
+ return withGallery({ content }, frameItems(frame, data.assetId), "Clean-up");
818
+ }
819
+ const image = await inlineImage(data.file);
820
+ if (image) content.push(image);
821
+ return withGallery({ content }, [mediaItem(data.file, "Cleaned up", { link })], "Clean-up");
822
+ })
823
+ );
824
+
774
825
  // ---------------------------------------------------------------------------
775
826
  // Animation
776
827
  // ---------------------------------------------------------------------------
@@ -920,12 +971,12 @@ export function createVoidlessServer(API_KEY) {
920
971
 
921
972
  server.tool(
922
973
  "generate_3d_model",
923
- "Generate a 3D model (GLB) from a prompt. 15 credits (lq) or 25 (hq).",
974
+ "Generate a 3D model (GLB) from a prompt. 15 credits (lq) or 60 (hq).",
924
975
  {
925
976
  prompt: z.string().describe("The model to make"),
926
977
  type: z.enum(["object", "character"]).default("object").describe("'character' makes a T-posed figure ready for rigging"),
927
978
  style: z.string().default("cartoon style").describe("e.g. 'cartoon style', 'low-poly', 'realistic'"),
928
- mode: z.enum(["lq", "hq"]).default("hq").describe("lq = fast draft, hq = detailed"),
979
+ mode: z.enum(["lq", "hq"]).default("hq").describe("lq = fast draft (15 credits), hq = detailed (60 credits)"),
929
980
  bodyType: z.enum(["humanoid", "quadruped"]).default("humanoid").describe("Characters only"),
930
981
  seed: z.number().int().min(0).default(0),
931
982
  },