voidless-mcp 1.6.14 → 1.6.15
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 +2 -0
- package/package.json +1 -1
- package/tools.js +74 -9
package/README.md
CHANGED
|
@@ -89,6 +89,8 @@ Or just the hosted connector, no key: `claude mcp add --transport http voidless
|
|
|
89
89
|
| --- | --- |
|
|
90
90
|
| `generate_image` | Sprite, character, portrait, prop, tileset, UI piece or artwork in a chosen model, style, palette, size and perspective; artwork can be 16:9 for backdrops |
|
|
91
91
|
| `edit_image` | Change an image with an instruction; the edit is added as a new frame |
|
|
92
|
+
| `make_normal_map` | A normal map for an image, an edit or every frame of an animation, for 2D lights in a game engine; free |
|
|
93
|
+
| `make_glow_map` | The light an edit added (lit windows, a night version's lamps) on its own, for an additive glow layer; free |
|
|
92
94
|
| `rotate_sprite` | An 8-way set (front, back, sides, diagonals) in one pass |
|
|
93
95
|
| `extract_parts` | Split a character into body parts for rigging |
|
|
94
96
|
| `upload_image` | Add an image you already have to your library |
|
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.15",
|
|
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>",
|
package/tools.js
CHANGED
|
@@ -27,7 +27,7 @@ import { Agent, setGlobalDispatcher } from "undici";
|
|
|
27
27
|
// server does.
|
|
28
28
|
setGlobalDispatcher(new Agent({ headersTimeout: 0, bodyTimeout: 0, connectTimeout: 30_000 }));
|
|
29
29
|
|
|
30
|
-
export const VERSION = "1.6.
|
|
30
|
+
export const VERSION = "1.6.15";
|
|
31
31
|
const BASE_URL = process.env.VOIDLESS_API_URL || "https://api.voidless.dev";
|
|
32
32
|
const CDN_URL = "https://cdn.voidless.dev";
|
|
33
33
|
// 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 MAX_INLINE_IMAGES = 8;
|
|
|
46
46
|
const GALLERY_URI = "ui://voidless/gallery.html";
|
|
47
47
|
const GALLERY_MIME = "text/html;profile=mcp-app";
|
|
48
48
|
const GALLERY_TOOLS = [
|
|
49
|
-
"generate_image", "upload_image", "edit_image", "rotate_sprite", "animate_sprite", "generate_animation_set",
|
|
49
|
+
"generate_image", "upload_image", "edit_image", "make_normal_map", "make_glow_map", "rotate_sprite", "animate_sprite", "generate_animation_set",
|
|
50
50
|
"flip_left_side", "generate_video", "generate_sfx", "generate_music", "generate_3d_model", "get_asset",
|
|
51
51
|
"list_assets", "extract_parts", "pixelate_image", "extend_video", "clean_up_sprite", "check_generation",
|
|
52
52
|
];
|
|
@@ -58,7 +58,7 @@ const GALLERY_HTML = readFileSync(new URL("./gallery.html", import.meta.url), "u
|
|
|
58
58
|
// job is still going the tool says so and check_generation fetches the result later (Strapi keeps
|
|
59
59
|
// it under the key: GET /generate/job/:key).
|
|
60
60
|
const LONG_PATHS = new Set([
|
|
61
|
-
"/generate/image", "/generate/image-edit", "/generate/image-rotations", "/generate/image-parts",
|
|
61
|
+
"/generate/image", "/generate/image-edit", "/generate/bbox-edit", "/generate/image-rotations", "/generate/image-parts",
|
|
62
62
|
"/generate/image-animation", "/generate/multi-animation", "/generate/video", "/generate/video-extend",
|
|
63
63
|
"/generate/sfx", "/generate/music", "/generate/3d-model", "/generate/model-rig", "/generate/model-animation",
|
|
64
64
|
]);
|
|
@@ -841,6 +841,65 @@ export function createVoidlessServer(API_KEY) {
|
|
|
841
841
|
tool(async (args) => framesResult(await apiPost("/generate/image-edit", { ...args, model: "p-image" }), args.id, "Edit"))
|
|
842
842
|
);
|
|
843
843
|
|
|
844
|
+
server.tool(
|
|
845
|
+
"make_normal_map",
|
|
846
|
+
"Make a normal map for an image asset, for 2D lights in a game engine: which way each pixel faces, so a " +
|
|
847
|
+
"moving light shades it. The shape comes from a depth model, the drawn detail from the picture. PNG at the " +
|
|
848
|
+
"picture's own size, OpenGL style (green = up: Godot and Unity read it as is; flip green for Unreal). " +
|
|
849
|
+
"Pass frameId (from get_asset) for an edit, or an animation (one map per frame); leave it out for the " +
|
|
850
|
+
"image itself. Free.",
|
|
851
|
+
{
|
|
852
|
+
id: assetId,
|
|
853
|
+
frameId: z
|
|
854
|
+
.number()
|
|
855
|
+
.int()
|
|
856
|
+
.optional()
|
|
857
|
+
.describe("Optional: the frame (an edit or an animation, from get_asset) to make it for. Empty = the image itself."),
|
|
858
|
+
},
|
|
859
|
+
writeTool("Make normal map"),
|
|
860
|
+
tool(async ({ id, frameId: fid }) => {
|
|
861
|
+
const data = await apiPost("/generate/normal-map", { id, ...(fid === undefined ? {} : { frameId: fid }) });
|
|
862
|
+
const keys = data.maps?.normals || (data.maps?.normal ? [data.maps.normal] : []);
|
|
863
|
+
const content = [
|
|
864
|
+
{
|
|
865
|
+
type: "text",
|
|
866
|
+
text:
|
|
867
|
+
`Normal map${keys.length > 1 ? `s (${keys.length}, one per frame)` : ""} saved on asset ${id} ` +
|
|
868
|
+
`(${APP_URL}/asset/${id}):\n` + keys.map((k) => `${CDN_URL}/${k}`).join("\n"),
|
|
869
|
+
},
|
|
870
|
+
];
|
|
871
|
+
const image = keys.length ? await inlineImage(keys[0]) : null;
|
|
872
|
+
if (image) content.push(image);
|
|
873
|
+
return { content, structuredContent: { summary: content[0].text } };
|
|
874
|
+
})
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
server.tool(
|
|
878
|
+
"make_glow_map",
|
|
879
|
+
"Make a glow map from an edit that switched lights on (or made a night version with lights glowing): the " +
|
|
880
|
+
"light the edit added over the picture it came from, on transparency, for an additive or emissive layer in " +
|
|
881
|
+
"a game. Same size as the picture. Pass the edit's frameId (from get_asset). Free.",
|
|
882
|
+
{
|
|
883
|
+
id: assetId,
|
|
884
|
+
frameId: z.number().int().describe("The edit (a frame from get_asset) whose light to keep"),
|
|
885
|
+
},
|
|
886
|
+
writeTool("Make glow map"),
|
|
887
|
+
tool(async ({ id, frameId: fid }) => {
|
|
888
|
+
const data = await apiPost("/generate/glow-map", { id, frameId: fid });
|
|
889
|
+
const keys = data.maps?.glows || (data.maps?.glow ? [data.maps.glow] : []);
|
|
890
|
+
const content = [
|
|
891
|
+
{
|
|
892
|
+
type: "text",
|
|
893
|
+
text: `Glow map${keys.length > 1 ? `s (${keys.length})` : ""} saved on asset ${id} (${APP_URL}/asset/${id}):\n` +
|
|
894
|
+
keys.map((k) => `${CDN_URL}/${k}`).join("\n"),
|
|
895
|
+
},
|
|
896
|
+
];
|
|
897
|
+
const image = keys.length ? await inlineImage(keys[0]) : null;
|
|
898
|
+
if (image) content.push(image);
|
|
899
|
+
return { content, structuredContent: { summary: content[0].text } };
|
|
900
|
+
})
|
|
901
|
+
);
|
|
902
|
+
|
|
844
903
|
server.tool(
|
|
845
904
|
"rotate_sprite",
|
|
846
905
|
"Turn a sprite into an 8-way set (front, back, sides and diagonals) in one pass. The result is a " +
|
|
@@ -1141,11 +1200,10 @@ export function createVoidlessServer(API_KEY) {
|
|
|
1141
1200
|
|
|
1142
1201
|
server.tool(
|
|
1143
1202
|
"rig_3d_model",
|
|
1144
|
-
"Add a skeleton to a
|
|
1145
|
-
"
|
|
1203
|
+
"Add a skeleton to a 3D model so it can be animated: a humanoid, a four-legged animal, or a prop's " +
|
|
1204
|
+
"moving parts (a chest lid, a door, a tree's trunk). Run before animate_3d_model. 30 credits.",
|
|
1146
1205
|
{
|
|
1147
1206
|
id: z.number().int().describe("3D model asset ID"),
|
|
1148
|
-
height_meters: z.number().min(0.2).max(10).default(1.7).describe("Character height in metres"),
|
|
1149
1207
|
},
|
|
1150
1208
|
writeTool("Rig 3D model"),
|
|
1151
1209
|
tool(async (args) => {
|
|
@@ -1156,11 +1214,18 @@ export function createVoidlessServer(API_KEY) {
|
|
|
1156
1214
|
|
|
1157
1215
|
server.tool(
|
|
1158
1216
|
"animate_3d_model",
|
|
1159
|
-
"Animate a rigged 3D model
|
|
1217
|
+
"Animate a rigged 3D model. Either a ready-made move (free; characters: idle, walk, run, jump, attack, " +
|
|
1218
|
+
"punch, kick, wave, hurt, death, cast, dance; four-legged animals: idle, walk, run, jump, attack, sit, hurt, " +
|
|
1219
|
+
"death), a move copied from another of the user's rigged models (free: from_id + from_animation), or a " +
|
|
1220
|
+
"description of any motion ('wag its tail', 'open the lid', 20 credits). Returns the clip, a GLB of the " +
|
|
1221
|
+
"skeleton's motion that plays on the rigged model.",
|
|
1160
1222
|
{
|
|
1161
1223
|
id: z.number().int().describe("Rigged 3D model asset ID"),
|
|
1162
|
-
|
|
1163
|
-
|
|
1224
|
+
preset: z.string().optional().describe("Ready-made move id, e.g. 'walk' (free)"),
|
|
1225
|
+
from_id: z.number().int().optional().describe("Copy a move from this other rigged model (free)"),
|
|
1226
|
+
from_animation: z.number().int().optional().describe("The animation id on from_id to copy"),
|
|
1227
|
+
prompt: z.string().optional().describe("Or describe the motion, e.g. 'a spinning kick' (20 credits)"),
|
|
1228
|
+
duration: z.number().min(1).max(10).optional().describe("Seconds, for a described motion (default: its natural length)"),
|
|
1164
1229
|
},
|
|
1165
1230
|
writeTool("Animate 3D model"),
|
|
1166
1231
|
tool(async (args) => {
|