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.
- package/README.md +14 -1
- package/package.json +9 -1
- 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.
|
|
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.
|
|
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
|
|
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
|
},
|