@iborymagic/aseprite-mcp 0.4.0 → 0.4.2

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 CHANGED
@@ -16,6 +16,14 @@ Supports fundamental Aseprite export workflow:
16
16
  Adds deeper control using Aseprite Lua scripting, enabling safe AI-driven editing operations such as:
17
17
  - `aseprite_list_lua_templates`: Lists available Lua templates
18
18
  - Predefined safe Lua automation(templates)
19
+ - `get_active_sprite_info`: Provides basic information about the currently active sprite
20
+ - `get_frame_info`: Inspects the animation timeline and frame structure of the active sprite
21
+ - `get_layer_list`: Returns a full structural overview of all layers in the active sprite, including groups
22
+ - `get_tag_list`: Lists all animation tags defined in the active sprite
23
+ - `get_is_layer_exists`: Checks whether a layer with a specific name exists
24
+ - `get_is_tag_exists`: Checks whether a specific animation tag exists
25
+ - `get_palette_info`: Inspects the current palette of the active sprite
26
+ - `get_selection_bounds`: Inspects the current selection state and its bounds
19
27
  - `remove_layer_by_name`: Removing specific layers
20
28
  - `recolor_palette`: Palette recoloring
21
29
  - `normalize_animation_speed`: Normalizing animation speed
@@ -11,12 +11,7 @@ export async function runAsepriteCommand(args, timeout = 10_000) {
11
11
  }
12
12
  catch (error) {
13
13
  if (error.killed === true && error.code === null) {
14
- return {
15
- command,
16
- stdout: "",
17
- stderr: "Aseprite command timed out",
18
- timedOut: true
19
- };
14
+ throw new Error(`Aseprite command timed out: ${command}`);
20
15
  }
21
16
  throw new Error(`Failed to run Aseprite command: ${command}\n${error instanceof Error ? error.message : String(error)}`);
22
17
  }
@@ -34,15 +34,13 @@ export function createToolHandlers() {
34
34
  if (dataAbsPath)
35
35
  args.push("--data", `"${dataAbsPath}"`);
36
36
  const result = await runAsepriteCommand(args);
37
- return successResult("aseprite_export_sheet", {
37
+ return successResult("aseprite_export_sheet", result.stdout.trim(), {
38
38
  command: result.command,
39
39
  inputFile: inputAbsPath,
40
40
  outputSheet: sheetAbsPath,
41
- sheetType,
41
+ sheetType: sheetType,
42
42
  dataFile: dataAbsPath ? dataAbsPath : undefined,
43
43
  tag: tag ? tag : undefined,
44
- stdout: result.stdout.trim(),
45
- stderr: result.stderr.trim(),
46
44
  });
47
45
  }
48
46
  catch (e) {
@@ -62,13 +60,11 @@ export function createToolHandlers() {
62
60
  if (tag)
63
61
  args.push("--tag", `"${tag}"`);
64
62
  const result = await runAsepriteCommand(args);
65
- return successResult("aseprite_export_frames", {
63
+ return successResult("aseprite_export_frames", result.stdout.trim(), {
66
64
  command: result.command,
67
65
  inputFile: inputAbsPath,
68
66
  outputPattern: outputAbsPath,
69
67
  tag: tag ? tag : undefined,
70
- stdout: result.stdout.trim(),
71
- stderr: result.stderr.trim(),
72
68
  });
73
69
  }
74
70
  catch (e) {
@@ -95,13 +91,11 @@ export function createToolHandlers() {
95
91
  catch (e) {
96
92
  metaText = `Failed to read metadata: ${e instanceof Error ? e.message : String(e)}`;
97
93
  }
98
- return successResult("aseprite_export_metadata", {
94
+ return successResult("aseprite_export_metadata", result.stdout.trim(), {
99
95
  command: result.command,
100
96
  inputFile: inputAbsPath,
101
97
  dataFile: dataAbsPath,
102
- format: format ? format : undefined,
103
- stdout: result.stdout.trim(),
104
- stderr: result.stderr.trim(),
98
+ format,
105
99
  metadata: metaText,
106
100
  });
107
101
  }
package/build/index.js CHANGED
@@ -5,14 +5,28 @@ import { createToolHandlers as createAsepriteToolHandlers, createToolSchemas as
5
5
  import { createToolHandlers as createLuaToolHandlers, createToolSchemas as createLuaToolSchemas } from "./lua/tools.js";
6
6
  import path from "node:path";
7
7
  import { readFileSync } from "node:fs";
8
+ import { fileURLToPath } from "node:url";
8
9
  let version;
9
10
  try {
10
- const packageJsonPath = path.join(process.cwd(), "package.json");
11
+ // Get the directory of the current file (build/index.js)
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ // Go up one level from build/ to package root
15
+ const packageJsonPath = path.join(__dirname, "..", "package.json");
11
16
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
12
17
  version = packageJson.version;
13
18
  }
14
19
  catch (error) {
15
- console.error("Failed to read package.json:", error);
20
+ // Fallback: try process.cwd() if the above fails
21
+ try {
22
+ const packageJsonPath = path.join(process.cwd(), "package.json");
23
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
24
+ version = packageJson.version;
25
+ }
26
+ catch (fallbackError) {
27
+ // If both fail, use default version
28
+ version = undefined;
29
+ }
16
30
  }
17
31
  const server = new McpServer({
18
32
  name: "aseprite-mcp",
@@ -28,9 +28,6 @@ export function createToolHandlers() {
28
28
  throw new Error(`Missing required params: ${missing.join(", ")}`);
29
29
  }
30
30
  const result = await runLuaScriptFile(template.scriptPath, params);
31
- if (result.timedOut) {
32
- throw new Error(`Lua script timed out while executing template: ${templateId}`);
33
- }
34
31
  const stderrTrimmed = result.stderr.trim();
35
32
  const stdoutTrimmed = result.stdout.trim();
36
33
  if (stderrTrimmed && stderrTrimmed.includes("ERROR:")) {
@@ -54,10 +51,9 @@ export function createToolHandlers() {
54
51
  saveOutput: ensureSafePath(saveOutput, { createDirIfNeeded: true })
55
52
  }
56
53
  });
57
- return successResult("auto_crop_transparent", {
58
- command: result.command,
59
- stdout: result.stdout,
60
- stderr: result.stderr
54
+ return successResult("auto_crop_transparent", result.stdout, {
55
+ inputFile: inputFile,
56
+ saveOutput: saveOutput,
61
57
  });
62
58
  }
63
59
  catch (err) {
@@ -73,10 +69,10 @@ export function createToolHandlers() {
73
69
  saveOutput: ensureSafePath(saveOutput, { createDirIfNeeded: true })
74
70
  }
75
71
  });
76
- return successResult("merge_visible_layers", {
72
+ return successResult("merge_visible_layers", result.stdout, {
77
73
  command: result.command,
78
- stdout: result.stdout,
79
- stderr: result.stderr
74
+ inputFile: inputFile,
75
+ saveOutput: saveOutput,
80
76
  });
81
77
  }
82
78
  catch (err) {
@@ -93,10 +89,11 @@ export function createToolHandlers() {
93
89
  targetDuration
94
90
  }
95
91
  });
96
- return successResult("normalize_animation_speed", {
92
+ return successResult("normalize_animation_speed", result.stdout, {
97
93
  command: result.command,
98
- stdout: result.stdout,
99
- stderr: result.stderr
94
+ inputFile: inputFile,
95
+ saveOutput: saveOutput,
96
+ targetDuration: targetDuration,
100
97
  });
101
98
  }
102
99
  catch (err) {
@@ -113,10 +110,11 @@ export function createToolHandlers() {
113
110
  inputFile
114
111
  }
115
112
  });
116
- return successResult("recolor_palette", {
113
+ return successResult("recolor_palette", result.stdout, {
117
114
  command: result.command,
118
- stdout: result.stdout,
119
- stderr: result.stderr
115
+ inputFile: inputFile,
116
+ saveOutput: saveOutput,
117
+ mapping: mapping,
120
118
  });
121
119
  }
122
120
  catch (err) {
@@ -125,6 +123,15 @@ export function createToolHandlers() {
125
123
  };
126
124
  const remove_layer_by_name = async ({ layerName, saveOutput, inputFile }) => {
127
125
  try {
126
+ const getIsLayerExistsResult = await get_is_layer_exists({
127
+ inputFile,
128
+ layerName
129
+ }, {});
130
+ const content = getIsLayerExistsResult.content[0];
131
+ const isLayerExist = JSON.parse(content.text).result === 'true';
132
+ if (!isLayerExist) {
133
+ return errorResult("remove_layer_by_name", `Layer not found: ${layerName}`);
134
+ }
128
135
  const result = await run_lua_template({
129
136
  templateId: "remove_layer_by_name",
130
137
  params: {
@@ -133,10 +140,11 @@ export function createToolHandlers() {
133
140
  inputFile
134
141
  }
135
142
  });
136
- return successResult("remove_layer_by_name", {
143
+ return successResult("remove_layer_by_name", result.stdout, {
137
144
  command: result.command,
138
- stdout: result.stdout,
139
- stderr: result.stderr
145
+ inputFile: inputFile,
146
+ layerName: layerName,
147
+ saveOutput: saveOutput,
140
148
  });
141
149
  }
142
150
  catch (err) {
@@ -145,6 +153,15 @@ export function createToolHandlers() {
145
153
  };
146
154
  const export_layer_only = async ({ layerName, outputDir, inputFile }) => {
147
155
  try {
156
+ const getIsLayerExistsResult = await get_is_layer_exists({
157
+ inputFile,
158
+ layerName
159
+ }, {});
160
+ const content = getIsLayerExistsResult.content[0];
161
+ const isLayerExist = JSON.parse(content.text).result === 'true';
162
+ if (!isLayerExist) {
163
+ return errorResult("export_layer_only", `Layer not found: ${layerName}`);
164
+ }
148
165
  const result = await run_lua_template({
149
166
  templateId: "export_layer_only",
150
167
  params: {
@@ -153,10 +170,11 @@ export function createToolHandlers() {
153
170
  inputFile
154
171
  }
155
172
  });
156
- return successResult("export_layer_only", {
173
+ return successResult("export_layer_only", result.stdout, {
157
174
  command: result.command,
158
- stdout: result.stdout,
159
- stderr: result.stderr
175
+ inputFile: inputFile,
176
+ layerName: layerName,
177
+ outputDir: outputDir,
160
178
  });
161
179
  }
162
180
  catch (err) {
@@ -165,6 +183,15 @@ export function createToolHandlers() {
165
183
  };
166
184
  const export_tag_frames = async ({ tag, outputDir, filenamePrefix, inputFile }) => {
167
185
  try {
186
+ const getIsTagExistsResult = await get_is_tag_exists({
187
+ inputFile,
188
+ tagName: tag
189
+ }, {});
190
+ const content = getIsTagExistsResult.content[0];
191
+ const isTagExist = JSON.parse(content.text).result === 'true';
192
+ if (!isTagExist) {
193
+ return errorResult("export_tag_frames", `Tag not found: ${tag}`);
194
+ }
168
195
  const result = await run_lua_template({
169
196
  templateId: "export_tag_frames",
170
197
  params: {
@@ -174,10 +201,12 @@ export function createToolHandlers() {
174
201
  inputFile
175
202
  }
176
203
  });
177
- return successResult("export_tag_frames", {
204
+ return successResult("export_tag_frames", result.stdout, {
178
205
  command: result.command,
179
- stdout: result.stdout,
180
- stderr: result.stderr
206
+ inputFile: inputFile,
207
+ tag: tag,
208
+ outputDir: outputDir,
209
+ filenamePrefix: filenamePrefix,
181
210
  });
182
211
  }
183
212
  catch (err) {
@@ -190,26 +219,11 @@ export function createToolHandlers() {
190
219
  templateId: "get_is_layer_exists",
191
220
  params: { layerName, inputFile }
192
221
  });
193
- // Parse the boolean result from stdout
194
- // Aseprite outputs Lua return values as JSON
195
- const stdoutTrimmed = result.stdout.trim();
196
- let exists;
197
- if (stdoutTrimmed === "") {
198
- // Empty stdout might mean false or error
199
- exists = false;
200
- }
201
- else {
202
- try {
203
- // Aseprite outputs Lua return values as JSON
204
- const parsed = JSON.parse(stdoutTrimmed);
205
- exists = parsed === true;
206
- }
207
- catch {
208
- // If not valid JSON, try string comparison
209
- exists = stdoutTrimmed.toLowerCase() === "true" || stdoutTrimmed === "1";
210
- }
211
- }
212
- return successResult("get_is_layer_exists", exists);
222
+ return successResult("get_is_layer_exists", result.stdout, {
223
+ command: result.command,
224
+ inputFile: inputFile,
225
+ layerName: layerName,
226
+ });
213
227
  }
214
228
  catch (err) {
215
229
  return errorResult("get_is_layer_exists", `Execution failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -221,26 +235,11 @@ export function createToolHandlers() {
221
235
  templateId: "get_is_tag_exists",
222
236
  params: { tagName, inputFile }
223
237
  });
224
- // Parse the boolean result from stdout
225
- // Aseprite outputs Lua return values as JSON
226
- const stdoutTrimmed = result.stdout.trim();
227
- let exists;
228
- if (stdoutTrimmed === "") {
229
- // Empty stdout might mean false or error
230
- exists = false;
231
- }
232
- else {
233
- try {
234
- // Aseprite outputs Lua return values as JSON
235
- const parsed = JSON.parse(stdoutTrimmed);
236
- exists = parsed === true;
237
- }
238
- catch {
239
- // If not valid JSON, try string comparison
240
- exists = stdoutTrimmed.toLowerCase() === "true" || stdoutTrimmed === "1";
241
- }
242
- }
243
- return successResult("get_is_tag_exists", exists);
238
+ return successResult("get_is_tag_exists", result.stdout, {
239
+ command: result.command,
240
+ inputFile: inputFile,
241
+ tagName: tagName,
242
+ });
244
243
  }
245
244
  catch (err) {
246
245
  return errorResult("get_is_tag_exists", `Execution failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -252,9 +251,10 @@ export function createToolHandlers() {
252
251
  templateId: "get_palette_info",
253
252
  params: { inputFile }
254
253
  });
255
- // Parse the JSON result from stdout
256
- const parsedResult = JSON.parse(result.stdout.trim());
257
- return successResult("get_palette_info", parsedResult);
254
+ return successResult("get_palette_info", result.stdout, {
255
+ command: result.command,
256
+ inputFile: inputFile,
257
+ });
258
258
  }
259
259
  catch (err) {
260
260
  return errorResult("get_palette_info", `Execution failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -266,9 +266,10 @@ export function createToolHandlers() {
266
266
  templateId: "get_selection_bounds",
267
267
  params: { inputFile }
268
268
  });
269
- // Parse the JSON result from stdout
270
- const parsedResult = JSON.parse(result.stdout.trim());
271
- return successResult("get_selection_bounds", parsedResult);
269
+ return successResult("get_selection_bounds", result.stdout, {
270
+ command: result.command,
271
+ inputFile: inputFile,
272
+ });
272
273
  }
273
274
  catch (err) {
274
275
  return errorResult("get_selection_bounds", `Execution failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -280,9 +281,10 @@ export function createToolHandlers() {
280
281
  templateId: "get_tag_list",
281
282
  params: { inputFile }
282
283
  });
283
- // Parse the JSON result from stdout
284
- const parsedResult = JSON.parse(result.stdout.trim());
285
- return successResult("get_tag_list", parsedResult);
284
+ return successResult("get_tag_list", result.stdout, {
285
+ command: result.command,
286
+ inputFile: inputFile,
287
+ });
286
288
  }
287
289
  catch (err) {
288
290
  return errorResult("get_tag_list", `Execution failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -294,9 +296,10 @@ export function createToolHandlers() {
294
296
  templateId: "get_layer_list",
295
297
  params: { inputFile }
296
298
  });
297
- // Parse the JSON result from stdout
298
- const parsedResult = JSON.parse(result.stdout.trim());
299
- return successResult("get_layer_list", parsedResult);
299
+ return successResult("get_layer_list", result.stdout, {
300
+ command: result.command,
301
+ inputFile: inputFile,
302
+ });
300
303
  }
301
304
  catch (err) {
302
305
  return errorResult("get_layer_list", `Execution failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -308,9 +311,10 @@ export function createToolHandlers() {
308
311
  templateId: "get_frame_info",
309
312
  params: { inputFile }
310
313
  });
311
- // Parse the JSON result from stdout
312
- const parsedResult = JSON.parse(result.stdout.trim());
313
- return successResult("get_frame_info", parsedResult);
314
+ return successResult("get_frame_info", result.stdout, {
315
+ command: result.command,
316
+ inputFile: inputFile,
317
+ });
314
318
  }
315
319
  catch (err) {
316
320
  return errorResult("get_frame_info", `Execution failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -322,9 +326,10 @@ export function createToolHandlers() {
322
326
  templateId: "get_active_sprite_info",
323
327
  params: { inputFile }
324
328
  });
325
- // Parse the JSON result from stdout
326
- const parsedResult = JSON.parse(result.stdout.trim());
327
- return successResult("get_active_sprite_info", parsedResult);
329
+ return successResult("get_active_sprite_info", result.stdout, {
330
+ command: result.command,
331
+ inputFile: inputFile,
332
+ });
328
333
  }
329
334
  catch (err) {
330
335
  return errorResult("get_active_sprite_info", `Execution failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -342,9 +347,6 @@ export function createToolHandlers() {
342
347
  luaFilePath = tempPath;
343
348
  }
344
349
  const result = await runLuaScriptFile(luaFilePath, params);
345
- if (result.timedOut) {
346
- return errorResult("aseprite_run_lua_script", `Lua script timed out while executing script: ${luaFilePath}`);
347
- }
348
350
  const stderrTrimmed = result.stderr.trim();
349
351
  const stdoutTrimmed = result.stdout.trim();
350
352
  if (stderrTrimmed && stderrTrimmed.includes("ERROR:")) {
@@ -353,10 +355,12 @@ export function createToolHandlers() {
353
355
  if (stdoutTrimmed && stdoutTrimmed.includes("ERROR:")) {
354
356
  return errorResult("aseprite_run_lua_script", `Script execution failed: ${stdoutTrimmed}`);
355
357
  }
356
- return successResult("aseprite_run_lua_script", {
358
+ return successResult("aseprite_run_lua_script", result.stdout, {
357
359
  command: result.command,
358
- stdout: stdoutTrimmed,
359
- stderr: stderrTrimmed
360
+ inputFile: params.inputFile,
361
+ scriptPath: luaFilePath,
362
+ scriptContent: scriptContent,
363
+ params: params,
360
364
  });
361
365
  }
362
366
  catch (err) {
package/build/util.js CHANGED
@@ -1,4 +1,4 @@
1
- export function successResult(tool, result) {
1
+ export function successResult(tool, result, params) {
2
2
  return {
3
3
  content: [
4
4
  {
@@ -6,6 +6,7 @@ export function successResult(tool, result) {
6
6
  text: JSON.stringify({
7
7
  success: true,
8
8
  tool,
9
+ params: params ?? null,
9
10
  result,
10
11
  }, null, 2)
11
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iborymagic/aseprite-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "MCP server for using Aseprite API",
5
5
  "main": "index.js",
6
6
  "type": "module",