voidless-mcp 1.6.17 → 1.7.1

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 (4) hide show
  1. package/README.md +24 -7
  2. package/index.js +36 -2
  3. package/package.json +3 -2
  4. package/tools.js +14 -9
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  Generate game assets from any MCP client (claude.ai, ChatGPT, Claude Desktop, Claude Code, Cursor, VS Code, Windsurf and others): sprites, animations, tilesets, sound effects, music, video and 3D models, saved straight to your [Voidless](https://voidless.dev) library and shown right in the chat.
4
4
 
5
- Two ways to connect: the **hosted connector** (paste a URL, sign in, nothing to install) or this **npm package** (runs locally with an API key). Setup guide: [voidless.dev/mcp](https://voidless.dev/mcp).
5
+ Two ways to connect: the **hosted connector** (paste a URL, sign in, nothing to install; recommended) or this **npm package** (runs locally with an API key). Setup guide: [voidless.dev/mcp](https://voidless.dev/mcp).
6
6
 
7
7
  ## Requirements
8
8
 
9
- - A Voidless account on **Voidless+**
10
- - For the npm package: Node.js 18 or newer and an API key (starts with `void_`), created at [voidless.dev/account](https://voidless.dev/account)
9
+ - For the hosted connector: a free Voidless account is enough
10
+ - For the npm package: Node.js 18 or newer and your own API key (starts with `void_`), created at [voidless.dev/account](https://voidless.dev/account). API keys are a **Voidless+** perk
11
11
 
12
- Every generation spends credits from your Voidless account. Tool descriptions list the cost tiers where they differ.
12
+ Every generation spends credits from your Voidless account, at the same prices as the website, and your plan's rules apply as they do there. Tool descriptions list the cost tiers where they differ. The free game-dev tools (below) cost no credits.
13
13
 
14
14
  ## Setup
15
15
 
@@ -23,7 +23,8 @@ https://mcp.voidless.dev/mcp
23
23
 
24
24
  - **Claude** (claude.ai and the Claude apps): **Settings → Connectors → Add custom connector**, name it Voidless and paste the URL.
25
25
  - **ChatGPT** (web, paid plans): **Settings → Apps & Connectors → Advanced settings**, turn on **Developer mode**, then **Create**, paste the URL and choose **OAuth**.
26
- - Any other MCP client that takes a remote server URL works the same way.
26
+ - **Claude Code**: `claude mcp add --transport http voidless https://mcp.voidless.dev/mcp`, then `/mcp` to sign in.
27
+ - **Cursor** and any other MCP client that takes a remote server URL work the same way.
27
28
 
28
29
  You'll be asked to sign in to Voidless and click **Allow**; no API key to copy. Results show inline in
29
30
  hosts that support MCP Apps: images, every facing of a rotation, animations playing, and audio and video
@@ -31,7 +32,7 @@ players. To disconnect, revoke the "… connector" key under API keys on [voidle
31
32
 
32
33
  ### Local server (npx)
33
34
 
34
- For clients that run a local server, or to work with image files on your own disk.
35
+ For clients that run a local server, or to work with image files on your own disk. Needs your own API key (Voidless+).
35
36
 
36
37
  Claude Desktop / Cursor / any client with a JSON config:
37
38
 
@@ -115,6 +116,21 @@ Or just the hosted connector, no key: `claude mcp add --transport http voidless
115
116
  | `generate_3d_model` / `rig_3d_model` / `animate_3d_model` | 3D model, rig it, animate it |
116
117
  | `render_3d_sprites` | A 3D model's move (or the model standing) as 2D sprites: 8, 4, platformer, isometric or front views, 16 frames, 32-512 px; free |
117
118
 
119
+ **Free game-dev tools** (no credits)
120
+
121
+ The same tools as [voidless.dev/tools](https://voidless.dev/tools), for any image, not just your library. Inputs are image URLs (a `cdn.voidless.dev` link from `get_asset` or a saved result works), image keys from your library (`assets/…png`, as `get_asset` lists them), or base64/data URIs, PNG or GIF, up to 2048x2048, 256 frames and 32 MB per call. Results come back in the chat: images as PNG/GIF, the `.tres` and atlas `.json` as text.
122
+
123
+ Results can also be saved to your library (free, private, counts toward your storage), which gives them an asset id and `cdn.voidless.dev` URLs to pass to the next tool instead of base64 (for example `generate_vfx` → `palette_swap` → `add_outline` → `animate_sprite`). `generate_vfx` saves its effect by default (with its recipe, so it can be rendered again; `save: false` skips it); `palette_swap`, `add_outline`, `pack_sprite_sheet`, `slice_sprite_sheet` and `make_gif` save only when you pass `save: true`. They run on Voidless's server: the hosted connector serves them, and the npm package forwards them there with your key (so they need a connection).
124
+
125
+ | Tool | What it does |
126
+ | --- | --- |
127
+ | `slice_sprite_sheet` | Cut a sheet into frames by columns/rows or cell size, or auto-detect the grid; optional trim, empty cells skipped |
128
+ | `pack_sprite_sheet` | Pack frames into one sheet plus a frames-hash atlas JSON (Phaser, PixiJS); padding, grid columns, power-of-two |
129
+ | `export_godot_spriteframes` | A Godot 4 SpriteFrames `.tres` and the sheet PNG it references, from frames or a sheet, with named animations (fps, loop) |
130
+ | `make_gif` | Frames or a sheet to an animated GIF: fps, loop, nearest-neighbour scale, transparency kept |
131
+ | `palette_swap` | Swap colours (exact from→to pairs, or nearest), or quantize to a preset/custom palette with dithering; lists an image's colours |
132
+ | `add_outline` | Outline a transparent sprite: colour, thickness, outside or inside, 4- or 8-connected |
133
+
118
134
  Generation tools create new content in your library; none deletes or overwrites existing assets. Each tool's description lists its credit cost.
119
135
 
120
136
  ## Example prompts
@@ -124,10 +140,11 @@ Generation tools create new content in your library; none deletes or overwrites
124
140
  - "Generate a top-down grass and dirt tileset in a gameboy palette."
125
141
  - "Give me a coin pickup sound effect and a short chiptune loop for the title screen."
126
142
  - "Create a low-poly treasure chest 3D model, rig it and animate it opening."
143
+ - "Slice this sheet into frames, make a 2x GIF of it, and give me a Godot SpriteFrames with idle (frames 0-3) and walk (4-9)."
127
144
 
128
145
  ## Privacy Policy
129
146
 
130
- This server sends your prompts, any images you pass in, and your API key to the Voidless API (api.voidless.dev) to run generations; results are stored in your Voidless account. It collects nothing else and keeps no local data. See the [Voidless Privacy Policy](https://voidless.dev/privacy-policy) for how Voidless collects, uses, stores, shares and retains data, and the [Terms of Service](https://voidless.dev/terms-of-service).
147
+ This server sends your prompts, any images you pass in, and your API key to the Voidless API (api.voidless.dev) to run generations; results are stored in your Voidless account. The free tools send their inputs to mcp.voidless.dev, which processes them in memory and stores nothing unless a result is saved to your library (`generate_vfx` by default, the others with `save: true`), where it is kept like any other asset of yours; each call is logged against your account (tool, time taken, success, result size) for usage statistics. It collects nothing else and keeps no local data. See the [Voidless Privacy Policy](https://voidless.dev/privacy-policy) for how Voidless collects, uses, stores, shares and retains data, and the [Terms of Service](https://voidless.dev/terms-of-service).
131
148
 
132
149
  ## Support
133
150
 
package/index.js CHANGED
@@ -6,18 +6,52 @@
6
6
  * Code, Cursor, VS Code. The hosted connector at https://mcp.voidless.dev/mcp serves the same tools
7
7
  * (tools.js) to clients that take a URL and sign in instead.
8
8
  *
9
+ * The free game-dev tools (slice/pack sheets, Godot SpriteFrames, GIFs, palette swap, outlines) are
10
+ * server-only code (Oct 30 2026): this package does not contain them. It lists them from the hosted
11
+ * connector and forwards each call there with the same key, so they show up here too.
12
+ *
9
13
  * Required environment variables:
10
- * VOIDLESS_API_KEY - void_... API key from your Voidless account (Voidless+)
14
+ * VOIDLESS_API_KEY - void_... API key from your Voidless account (hand-made keys are Voidless+;
15
+ * on a free account, use the hosted connector instead)
16
+ * Optional:
17
+ * VOIDLESS_MCP_URL - the hosted connector the free tools are forwarded to, default https://mcp.voidless.dev/mcp
11
18
  */
12
19
 
13
20
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
- import { createVoidlessServer } from "./tools.js";
21
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
22
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
23
+ import { z } from "zod";
24
+ import { createVoidlessServer, VERSION } from "./tools.js";
15
25
 
16
26
  const API_KEY = process.env.VOIDLESS_API_KEY || "";
17
27
  if (!API_KEY) {
18
28
  console.error("[voidless-mcp] VOIDLESS_API_KEY is not set (expected a void_... key)");
19
29
  process.exit(1);
20
30
  }
31
+ const REMOTE_URL = process.env.VOIDLESS_MCP_URL || "https://mcp.voidless.dev/mcp";
21
32
 
22
33
  const server = createVoidlessServer(API_KEY, "stdio");
23
34
  await server.connect(new StdioServerTransport());
35
+
36
+ // After connecting, so a slow or unreachable network never delays startup: the tools appear when the
37
+ // listing arrives (the client is told the tool list changed). Generic on purpose: whatever the hosted
38
+ // server marks as free ("voidless/free" in _meta) is forwarded, so new free tools need no release here.
39
+ try {
40
+ const remote = new Client({ name: "voidless-mcp-local", version: VERSION });
41
+ await remote.connect(new StreamableHTTPClientTransport(new URL(REMOTE_URL), {
42
+ requestInit: { headers: { Authorization: `Bearer ${API_KEY}`, "X-Voidless-Client": `mcp/${VERSION} (stdio)` } },
43
+ }));
44
+ const { tools } = await remote.listTools();
45
+ for (const t of tools.filter((t) => t._meta?.["voidless/free"])) {
46
+ if (server._registeredTools?.[t.name]) continue;
47
+ server.registerTool(t.name, {
48
+ title: t.title || t.annotations?.title,
49
+ description: t.description,
50
+ inputSchema: z.fromJSONSchema(t.inputSchema),
51
+ annotations: t.annotations,
52
+ _meta: t._meta,
53
+ }, (args) => remote.callTool({ name: t.name, arguments: args }, undefined, { timeout: 180_000 }));
54
+ }
55
+ } catch (err) {
56
+ console.error(`[voidless-mcp] free tools unavailable (could not reach ${REMOTE_URL}): ${err?.message || err}`);
57
+ }
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.17",
4
+ "version": "1.7.1",
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>",
@@ -39,7 +39,8 @@
39
39
  ],
40
40
  "scripts": {
41
41
  "start": "node index.js",
42
- "start:http": "node http.js"
42
+ "start:http": "node http.js",
43
+ "test": "cd free && npm test"
43
44
  },
44
45
  "dependencies": {
45
46
  "@modelcontextprotocol/sdk": "^1.12.1",
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.17";
30
+ export const VERSION = "1.7.0";
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
@@ -286,7 +286,8 @@ export function createVoidlessServer(API_KEY, transport = "stdio") {
286
286
  const content = [
287
287
  {
288
288
  type: "text",
289
- text: [`${label} added to asset ${assetId} (${APP_URL}/asset/${assetId}).`, money(data)]
289
+ // note: e.g. an end frame of another size was scaled to the start image's.
290
+ text: [`${label} added to asset ${assetId} (${APP_URL}/asset/${assetId}).`, data.note, money(data)]
290
291
  .filter(Boolean)
291
292
  .join("\n"),
292
293
  },
@@ -425,8 +426,8 @@ export function createVoidlessServer(API_KEY, transport = "stdio") {
425
426
  .string()
426
427
  .default("")
427
428
  .describe(
428
- "Work from one image of an existing frame instead of the asset's base: pass an image key exactly as " +
429
- "get_asset lists it. Empty = the asset's current base image."
429
+ "Work from one image instead of the asset's base: an image key from any of your assets (as get_asset " +
430
+ "lists it), or its cdn.voidless.dev URL. Empty = the asset's current base image."
430
431
  );
431
432
  const frameId = z
432
433
  .number()
@@ -628,7 +629,7 @@ export function createVoidlessServer(API_KEY, transport = "stdio") {
628
629
  "get_asset",
629
630
  "Show one asset with every frame: frame ids, types (animation, rotations - 8-way, edit - text, layer…), " +
630
631
  "names, facings and image keys. Frame ids are what frameIndex/items/frameId take elsewhere; image keys " +
631
- "are what referenceFile and end_frame take. Free.",
632
+ "are what referenceFile and end_frame take, on this asset or any other of yours. Free.",
632
633
  { id: assetId, preview: z.boolean().default(true).describe("Include the base image inline") },
633
634
  readTool("Get asset"),
634
635
  tool(async ({ id, preview }) => {
@@ -753,7 +754,7 @@ export function createVoidlessServer(API_KEY, transport = "stdio") {
753
754
  "Generate a game sprite/asset image. Infer every field from the request: art STYLE, colour PALETTE, " +
754
755
  "asset TYPE, output SIZE and PERSPECTIVE; for characters also facing/pose/bodyType. Use the default model " +
755
756
  "(flux2-klein, 1 credit) unless the user names another. Costs by model: flux2-klein 1, flux-juggernaut 1, " +
756
- "seedream-5-flash 2, gpt-image-2.5 5, retro-diffusion 10, nano-banana-2 15 credits (a reference image makes it " +
757
+ "seedream-5-flash 10, gpt-image-2.5 5, retro-diffusion 10, nano-banana-2 15 credits (a reference image makes it " +
757
758
  "2 and uses flux2-klein). Call it once per request and " +
758
759
  "show the result; ask the user before generating again (each call spends credits).",
759
760
  {
@@ -838,7 +839,7 @@ export function createVoidlessServer(API_KEY, transport = "stdio") {
838
839
  prompt: z.string().describe("What to change"),
839
840
  referenceFile,
840
841
  frameId,
841
- images: z.array(z.string()).max(4).default([]).describe("Optional extra reference images: image keys from get_asset or https URLs"),
842
+ images: z.array(z.string()).max(4).default([]).describe("Optional extra reference images: image keys from any of your assets (get_asset lists them), or their cdn.voidless.dev URLs. Other links are refused: upload the picture first"),
842
843
  remove_bg: z.boolean().default(true).describe("Remove the background from the result, as the app's editor does. Set false for artwork or a scene that should keep its background"),
843
844
  },
844
845
  writeTool("Edit image"),
@@ -918,7 +919,7 @@ export function createVoidlessServer(API_KEY, transport = "stdio") {
918
919
  server.tool(
919
920
  "extract_parts",
920
921
  "Split a character sprite into separate body parts (head, torso, arms, legs…) as 'layer' frames, for " +
921
- "rigging or cut-out animation. 5 credits. Call get_asset afterwards for the parts' frame ids.",
922
+ "rigging or cut-out animation. 10 credits. Call get_asset afterwards for the parts' frame ids.",
922
923
  {
923
924
  id: assetId,
924
925
  prompt: z.string().default("").describe("Optional: which parts to cut out. Empty = the standard rig parts"),
@@ -1043,7 +1044,11 @@ export function createVoidlessServer(API_KEY, transport = "stdio") {
1043
1044
  id: assetId,
1044
1045
  prompt: z.string().describe("The motion, e.g. 'walk cycle', 'swings sword overhead'"),
1045
1046
  referenceFile,
1046
- end_frame: z.string().default("").describe("Optional image key for the clip to end on (e.g. to loop back to a pose). Empty = free ending"),
1047
+ end_frame: z.string().default("").describe(
1048
+ "Optional image for the clip to end on: an image key from any of your assets (get_asset lists them), or its " +
1049
+ "cdn.voidless.dev URL, e.g. the start image to loop, or another sprite's pose. Empty = free ending. An end " +
1050
+ "image of another size is scaled to the start image's; one of a very different shape is refused (nothing charged)"
1051
+ ),
1047
1052
  frameId,
1048
1053
  },
1049
1054
  writeTool("Animate sprite"),