@iborymagic/aseprite-mcp 0.1.0

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 ADDED
@@ -0,0 +1,61 @@
1
+ # Aseprite-MCP
2
+ This server automates Aseprite workflows using the Model Context Protocol (MCP).
3
+ It enables AI, chat assistants, and automation pipelines to directly execute Aseprite tasks such as sprite sheet export, frame extraction, and metadata output.
4
+ *Lua-based automation and high-level sprite/tile generation features are not included yet.
5
+ *Aseprite must be installed in order to use this MCP server.
6
+
7
+ ## How to use
8
+ 1) Run directly with npx
9
+ ```bash
10
+ npx -y aseprite-mcp
11
+ ```
12
+
13
+ 2) Local Build & Run (for development)
14
+ ```bash
15
+ npm install
16
+ npm run build
17
+ npx aseprite-mcp
18
+ ```
19
+
20
+ ### Using with ChatGPT
21
+ Add the following to your mcp.json
22
+ ```json
23
+ {
24
+ "servers": {
25
+ "aseprite-mcp": {
26
+ "command": "npx",
27
+ "args": ["-y", "aseprite-mcp"]
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ### Using with Claude
34
+ Add the following to your servers.json
35
+ ```json
36
+ {
37
+ "aseprite-mcp": {
38
+ "command": "npx",
39
+ "args": ["-y", "aseprite-mcp"]
40
+ }
41
+ }
42
+ ```
43
+
44
+ ### Using with Cursor
45
+ Add the following to your .cursor.json
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "aseprite-mcp": {
50
+ "command": "npx",
51
+ "args": ["-y", "aseprite-mcp"]
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## Tools
58
+ - `aseprite_check_environment`: Checks Aseprite installation status, executable path, and version
59
+ - `aseprite_export_sheet`: Exports a sprite sheet as PNG + JSON
60
+ - `aseprite_export_frames`: Exports each animation frame as an individual PNG file
61
+ - `aseprite_export_metadata`: Exports Aseprite metadata in JSON format
@@ -0,0 +1,15 @@
1
+ import { exec } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { resolveAsepritePath } from "./env.js";
4
+ const execAsync = promisify(exec);
5
+ export async function runAsepriteCommand(args) {
6
+ const path = await resolveAsepritePath();
7
+ const command = `"${path}" ${args.join(" ")}`;
8
+ try {
9
+ const { stdout, stderr } = await execAsync(command);
10
+ return { command, stdout, stderr };
11
+ }
12
+ catch (error) {
13
+ throw new Error(`Failed to run Aseprite command: ${command}\n${error instanceof Error ? error.message : String(error)}`);
14
+ }
15
+ }
@@ -0,0 +1,120 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { exec } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+ import * as os from "node:os";
5
+ import * as path from "node:path";
6
+ const execAsync = promisify(exec);
7
+ const candidates = {
8
+ win32: [
9
+ "C:\\Program Files\\Aseprite\\aseprite.exe",
10
+ "C:\\Program Files (x86)\\Aseprite\\aseprite.exe",
11
+ ],
12
+ darwin: [
13
+ "/Applications/Aseprite.app/Contents/MacOS/aseprite",
14
+ "/usr/local/bin/aseprite",
15
+ "/opt/homebrew/bin/aseprite",
16
+ ],
17
+ linux: [
18
+ "/usr/bin/aseprite",
19
+ "/usr/local/bin/aseprite",
20
+ ]
21
+ };
22
+ function getSteamVdfPaths() {
23
+ switch (process.platform) {
24
+ case "win32":
25
+ return [
26
+ path.join(process.env.PROGRAMFILESX86 ??
27
+ "C:\\Program Files (x86)", "Steam/steamapps/libraryfolders.vdf"),
28
+ path.join(process.env.PROGRAMFILES ??
29
+ "C:\\Program Files", "Steam/steamapps/libraryfolders.vdf")
30
+ ];
31
+ case "darwin":
32
+ return [
33
+ path.join(os.homedir(), "Library/Application Support/Steam/steamapps/libraryfolders.vdf")
34
+ ];
35
+ default:
36
+ // linux
37
+ return [
38
+ path.join(os.homedir(), ".steam/steam/steamapps/libraryfolders.vdf"),
39
+ path.join(os.homedir(), ".local/share/Steam/steamapps/libraryfolders.vdf")
40
+ ];
41
+ }
42
+ }
43
+ function parseSteamLibraries(vdfContent) {
44
+ const lines = vdfContent.split("\n");
45
+ const paths = [];
46
+ for (const line of lines) {
47
+ const match = line.match(/"(\d+)"\s+"(.+?)"/);
48
+ if (match) {
49
+ let p = match[2];
50
+ if (process.platform === "win32") {
51
+ p = p.replace(/\\\\/g, "\\");
52
+ }
53
+ paths.push(p);
54
+ }
55
+ // libraryfolders 2.0
56
+ const kvMatch = line.match(/"path"\s+"(.+?)"/);
57
+ if (kvMatch) {
58
+ let p = kvMatch[1];
59
+ if (process.platform === "win32") {
60
+ p = p.replace(/\\\\/g, "\\");
61
+ }
62
+ paths.push(p);
63
+ }
64
+ }
65
+ return Array.from(new Set(paths));
66
+ }
67
+ export function getSteamAsepritePaths() {
68
+ const files = getSteamVdfPaths();
69
+ for (const file of files) {
70
+ if (!existsSync(file))
71
+ continue;
72
+ try {
73
+ const content = readFileSync(file, "utf8");
74
+ const libs = parseSteamLibraries(content);
75
+ const paths = [];
76
+ for (const lib of libs) {
77
+ switch (process.platform) {
78
+ case "win32":
79
+ paths.push(path.join(lib, "steamapps/common/Aseprite/aseprite.exe"));
80
+ break;
81
+ case "darwin":
82
+ paths.push(path.join(lib, "steamapps/common/Aseprite/Aseprite.app/Contents/MacOS/aseprite"));
83
+ break;
84
+ default: // linux
85
+ paths.push(path.join(lib, "steamapps/common/Aseprite/aseprite"));
86
+ break;
87
+ }
88
+ }
89
+ return paths;
90
+ }
91
+ catch {
92
+ continue;
93
+ }
94
+ }
95
+ return [];
96
+ }
97
+ export async function resolveAsepritePath() {
98
+ try {
99
+ const platform = process.platform;
100
+ const cmd = platform === "win32" ? "where aseprite" : "which aseprite";
101
+ const { stdout } = await execAsync(cmd);
102
+ const found = stdout.split("\n")[0].trim();
103
+ if (found && existsSync(found))
104
+ return found;
105
+ }
106
+ catch { }
107
+ const defaults = candidates[process.platform] ??
108
+ [];
109
+ for (const path of defaults) {
110
+ if (existsSync(path))
111
+ return path;
112
+ }
113
+ const steamPaths = getSteamAsepritePaths();
114
+ for (const path of steamPaths) {
115
+ if (existsSync(path)) {
116
+ return path;
117
+ }
118
+ }
119
+ throw new Error(`Aseprite not found. Please install it or add to PATH.\nDetected OS: ${process.platform}`);
120
+ }
@@ -0,0 +1,33 @@
1
+ import { existsSync, mkdirSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ export function normalizePath(p) {
4
+ if (!p || typeof p !== "string") {
5
+ throw new Error("Path must be a non-empty string");
6
+ }
7
+ return path.resolve(p);
8
+ }
9
+ export function mkdirSafe(dir) {
10
+ try {
11
+ mkdirSync(dir, { recursive: true });
12
+ }
13
+ catch (err) {
14
+ throw new Error(`Failed to create directory: ${dir}`);
15
+ }
16
+ }
17
+ export function ensureSafePath(p, options = {}) {
18
+ const absPath = normalizePath(p);
19
+ const parentDir = path.dirname(absPath);
20
+ if (options.mustExist) {
21
+ if (!existsSync(absPath)) {
22
+ throw new Error(`File does not exist: ${absPath}`);
23
+ }
24
+ const stat = statSync(absPath);
25
+ if (!stat.isFile()) {
26
+ throw new Error(`Not a file: ${absPath}`);
27
+ }
28
+ }
29
+ if (options.createDirIfNeeded) {
30
+ mkdirSafe(parentDir);
31
+ }
32
+ return absPath;
33
+ }
@@ -0,0 +1,96 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { exec } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+ import * as os from "node:os";
5
+ import * as path from "node:path";
6
+ const execAsync = promisify(exec);
7
+ const defaultCandidates = {
8
+ win32: [
9
+ "C:\\Program Files\\Aseprite\\aseprite.exe",
10
+ "C:\\Program Files (x86)\\Aseprite\\aseprite.exe"
11
+ ],
12
+ darwin: [
13
+ "/Applications/Aseprite.app/Contents/MacOS/aseprite",
14
+ "/usr/local/bin/aseprite",
15
+ "/opt/homebrew/bin/aseprite"
16
+ ],
17
+ linux: [
18
+ "/usr/bin/aseprite",
19
+ "/usr/local/bin/aseprite"
20
+ ]
21
+ };
22
+ function getSteamVdfPaths() {
23
+ switch (process.platform) {
24
+ case "win32":
25
+ return [
26
+ path.join(process.env.PROGRAMFILESX86 ??
27
+ "C:\\Program Files (x86)", "Steam/steamapps/libraryfolders.vdf"),
28
+ path.join(process.env.PROGRAMFILES ??
29
+ "C:\\Program Files", "Steam/steamapps/libraryfolders.vdf")
30
+ ];
31
+ case "darwin":
32
+ return [
33
+ path.join(os.homedir(), "Library/Application Support/Steam/steamapps/libraryfolders.vdf")
34
+ ];
35
+ default:
36
+ // linux
37
+ return [
38
+ path.join(os.homedir(), ".steam/steam/steamapps/libraryfolders.vdf"),
39
+ path.join(os.homedir(), ".local/share/Steam/steamapps/libraryfolders.vdf")
40
+ ];
41
+ }
42
+ }
43
+ function parseSteamLibraries(vdfContent) {
44
+ const lines = vdfContent.split("\n");
45
+ const paths = [];
46
+ for (const line of lines) {
47
+ const match = line.match(/"(\d+)"\s+"(.+?)"/);
48
+ if (match) {
49
+ let p = match[2];
50
+ if (process.platform === "win32") {
51
+ p = p.replace(/\\\\/g, "\\");
52
+ }
53
+ paths.push(p);
54
+ }
55
+ // libraryfolders 2.0
56
+ const kvMatch = line.match(/"path"\s+"(.+?)"/);
57
+ if (kvMatch) {
58
+ let p = kvMatch[1];
59
+ if (process.platform === "win32") {
60
+ p = p.replace(/\\\\/g, "\\");
61
+ }
62
+ paths.push(p);
63
+ }
64
+ }
65
+ return Array.from(new Set(paths));
66
+ }
67
+ export function getSteamAsepritePaths() {
68
+ const files = getSteamVdfPaths();
69
+ for (const file of files) {
70
+ if (!existsSync(file))
71
+ continue;
72
+ try {
73
+ const content = readFileSync(file, "utf8");
74
+ const libs = parseSteamLibraries(content);
75
+ const paths = [];
76
+ for (const lib of libs) {
77
+ switch (process.platform) {
78
+ case "win32":
79
+ paths.push(path.join(lib, "steamapps/common/Aseprite/aseprite.exe"));
80
+ break;
81
+ case "darwin":
82
+ paths.push(path.join(lib, "steamapps/common/Aseprite/Aseprite.app/Contents/MacOS/aseprite"));
83
+ break;
84
+ default: // linux
85
+ paths.push(path.join(lib, "steamapps/common/Aseprite/aseprite"));
86
+ break;
87
+ }
88
+ }
89
+ return paths;
90
+ }
91
+ catch {
92
+ continue;
93
+ }
94
+ }
95
+ return [];
96
+ }
package/build/index.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { createToolHandlers, createToolSchemas } from "./tools.js";
5
+ const server = new McpServer({
6
+ name: "aseprite-mcp",
7
+ version: "0.1.0"
8
+ });
9
+ const toolSchemas = createToolSchemas();
10
+ const toolHandlers = createToolHandlers();
11
+ server.registerTool("aseprite_check_environment", {
12
+ description: "Check the environment of Aseprite",
13
+ inputSchema: undefined,
14
+ outputSchema: undefined,
15
+ }, toolHandlers.aseprite_check_environment);
16
+ server.registerTool("aseprite_export_sheet", {
17
+ description: "Export Aseprite file to sprite sheet image",
18
+ inputSchema: toolSchemas.aseprite_export_sheet,
19
+ outputSchema: toolSchemas.aseprite_output_result,
20
+ }, toolHandlers.aseprite_export_sheet);
21
+ server.registerTool("aseprite_export_frames", {
22
+ description: "Export each frame of Aseprite file",
23
+ inputSchema: toolSchemas.aseprite_export_frames,
24
+ outputSchema: toolSchemas.aseprite_output_result,
25
+ }, toolHandlers.aseprite_export_frames);
26
+ server.registerTool("aseprite_export_metadata", {
27
+ description: "Export metadata json from Aseprite file",
28
+ inputSchema: toolSchemas.aseprite_export_metadata,
29
+ outputSchema: toolSchemas.aseprite_output_result,
30
+ }, toolHandlers.aseprite_export_metadata);
31
+ async function main() {
32
+ const transport = new StdioServerTransport();
33
+ await server.connect(transport);
34
+ console.log("Aseprite MCP server started");
35
+ }
36
+ main().catch(err => {
37
+ console.error("MCP Error:", err);
38
+ process.exit(1);
39
+ });
@@ -0,0 +1,16 @@
1
+ import { resolveAsepritePath } from "./aseprite/env.js";
2
+ import { runAsepriteCommand } from "./aseprite/cli.js";
3
+ async function main() {
4
+ try {
5
+ const path = await resolveAsepritePath();
6
+ const { stdout: version } = await runAsepriteCommand(["--version"]);
7
+ console.log("✅ Aseprite OK");
8
+ console.log("Path:", path);
9
+ console.log("Version:", version);
10
+ }
11
+ catch (err) {
12
+ console.error("❌ Failed");
13
+ console.error(err.message ?? err);
14
+ }
15
+ }
16
+ main();
@@ -0,0 +1,177 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod/v4";
3
+ import { readFileSync as readFileSyncNode } from "node:fs";
4
+ import { resolveAsepritePath as resolveAsepritePathDefault } from "./aseprite/env.js";
5
+ import { runAsepriteCommand as runAsepriteCommandDefault } from "./aseprite/cli.js";
6
+ import { ensureSafePath as ensureSafePathDefault } from "./aseprite/path.js";
7
+ import { errorResult as errorResultDefault, successResult as successResultDefault } from "./util.js";
8
+ export function createServer(deps = {}) {
9
+ const d = {
10
+ resolveAsepritePath: resolveAsepritePathDefault,
11
+ runAsepriteCommand: runAsepriteCommandDefault,
12
+ ensureSafePath: ensureSafePathDefault,
13
+ readFileSync: readFileSyncNode,
14
+ successResult: successResultDefault,
15
+ errorResult: errorResultDefault,
16
+ ...deps,
17
+ };
18
+ const server = new McpServer({
19
+ name: "aseprite-mcp",
20
+ version: "0.1.0",
21
+ });
22
+ const tools = new Map();
23
+ tools.set("aseprite_check_environment", server.registerTool("aseprite_check_environment", {
24
+ description: "Check the environment of Aseprite",
25
+ inputSchema: undefined,
26
+ outputSchema: undefined,
27
+ }, async () => {
28
+ try {
29
+ const asepritePath = await d.resolveAsepritePath();
30
+ const { stdout: version } = await d.runAsepriteCommand(["--version"]);
31
+ return d.successResult("aseprite_check_environment", {
32
+ path: asepritePath,
33
+ version,
34
+ });
35
+ }
36
+ catch (e) {
37
+ return d.errorResult("aseprite_check_environment", e instanceof Error ? e.message : String(e));
38
+ }
39
+ }));
40
+ tools.set("aseprite_export_sheet", server.registerTool("aseprite_export_sheet", {
41
+ description: "Export Aseprite file to sprite sheet image",
42
+ inputSchema: z.object({
43
+ inputFile: z.string(),
44
+ outputSheet: z.string(),
45
+ sheetType: z.enum(["rows", "columns", "packed"]).optional().default("packed"),
46
+ dataFile: z.string().optional(),
47
+ tag: z.string().optional(),
48
+ }),
49
+ outputSchema: z.object({
50
+ content: z.array(z.object({
51
+ type: z.literal("text"),
52
+ text: z.string(),
53
+ })),
54
+ }),
55
+ }, async ({ inputFile, outputSheet, sheetType, dataFile, tag }) => {
56
+ try {
57
+ const inputAbsPath = d.ensureSafePath(inputFile, { mustExist: true });
58
+ const sheetAbsPath = d.ensureSafePath(outputSheet, { createDirIfNeeded: true });
59
+ const dataAbsPath = dataFile
60
+ ? d.ensureSafePath(dataFile, { createDirIfNeeded: true })
61
+ : undefined;
62
+ const args = [
63
+ "--batch",
64
+ `"${inputAbsPath}"`,
65
+ "--sheet",
66
+ `"${sheetAbsPath}"`,
67
+ "--sheet-type",
68
+ sheetType,
69
+ ];
70
+ if (tag)
71
+ args.push("--tag", `"${tag}"`);
72
+ if (dataAbsPath)
73
+ args.push("--data", `"${dataAbsPath}"`);
74
+ const result = await d.runAsepriteCommand(args);
75
+ return d.successResult("aseprite_export_sheet", {
76
+ command: result.command,
77
+ inputFile: inputAbsPath,
78
+ outputSheet: sheetAbsPath,
79
+ sheetType,
80
+ dataFile: dataAbsPath ? dataAbsPath : undefined,
81
+ tag: tag ? tag : undefined,
82
+ stdout: result.stdout.trim(),
83
+ stderr: result.stderr.trim(),
84
+ });
85
+ }
86
+ catch (e) {
87
+ return d.errorResult("aseprite_export_sheet", e instanceof Error ? e.message : String(e));
88
+ }
89
+ }));
90
+ tools.set("aseprite_export_frames", server.registerTool("aseprite_export_frames", {
91
+ description: "Export each frame of Aseprite file",
92
+ inputSchema: z.object({
93
+ inputFile: z.string(),
94
+ outputPattern: z.string(),
95
+ tag: z.string().optional(),
96
+ }),
97
+ outputSchema: z.object({
98
+ content: z.array(z.object({
99
+ type: z.literal("text"),
100
+ text: z.string(),
101
+ })),
102
+ }),
103
+ }, async ({ inputFile, outputPattern, tag }) => {
104
+ try {
105
+ const inputAbsPath = d.ensureSafePath(inputFile, { mustExist: true });
106
+ const outputAbsPath = d.ensureSafePath(outputPattern, { createDirIfNeeded: true });
107
+ const args = [
108
+ "--batch",
109
+ `"${inputAbsPath}"`,
110
+ "--save-as",
111
+ `"${outputAbsPath}"`,
112
+ ];
113
+ if (tag)
114
+ args.push("--tag", `"${tag}"`);
115
+ const result = await d.runAsepriteCommand(args);
116
+ return d.successResult("aseprite_export_frames", {
117
+ command: result.command,
118
+ inputFile: inputAbsPath,
119
+ outputPattern: outputAbsPath,
120
+ tag: tag ? tag : undefined,
121
+ stdout: result.stdout.trim(),
122
+ stderr: result.stderr.trim(),
123
+ });
124
+ }
125
+ catch (e) {
126
+ return d.errorResult("aseprite_export_frames", e instanceof Error ? e.message : String(e));
127
+ }
128
+ }));
129
+ tools.set("aseprite_export_metadata", server.registerTool("aseprite_export_metadata", {
130
+ description: "Export metadata json from Aseprite file",
131
+ inputSchema: z.object({
132
+ inputFile: z.string(),
133
+ dataFile: z.string(),
134
+ format: z.string().optional(),
135
+ }),
136
+ outputSchema: z.object({
137
+ content: z.array(z.object({
138
+ type: z.literal("text"),
139
+ text: z.string(),
140
+ })),
141
+ }),
142
+ }, async ({ inputFile, dataFile, format }) => {
143
+ try {
144
+ const inputAbsPath = d.ensureSafePath(inputFile, { mustExist: true });
145
+ const dataAbsPath = d.ensureSafePath(dataFile, { createDirIfNeeded: true });
146
+ const args = [
147
+ "--batch",
148
+ `"${inputAbsPath}"`,
149
+ "--data",
150
+ `"${dataAbsPath}"`,
151
+ ];
152
+ if (format)
153
+ args.push("--format", `"${format}"`);
154
+ const result = await d.runAsepriteCommand(args);
155
+ let metaText = "";
156
+ try {
157
+ metaText = d.readFileSync(dataAbsPath, "utf8");
158
+ }
159
+ catch (e) {
160
+ metaText = `Failed to read metadata: ${e instanceof Error ? e.message : String(e)}`;
161
+ }
162
+ return d.successResult("aseprite_export_metadata", {
163
+ command: result.command,
164
+ inputFile: inputAbsPath,
165
+ dataFile: dataAbsPath,
166
+ format: format ? format : undefined,
167
+ stdout: result.stdout.trim(),
168
+ stderr: result.stderr.trim(),
169
+ metadata: metaText,
170
+ });
171
+ }
172
+ catch (e) {
173
+ return d.errorResult("aseprite_export_metadata", e instanceof Error ? e.message : String(e));
174
+ }
175
+ }));
176
+ return { server, tools };
177
+ }
package/build/tools.js ADDED
@@ -0,0 +1,145 @@
1
+ import z from "zod";
2
+ import { resolveAsepritePath } from "./aseprite/env.js";
3
+ import { runAsepriteCommand } from "./aseprite/cli.js";
4
+ import { errorResult, successResult } from "./util.js";
5
+ import { ensureSafePath } from "./aseprite/path.js";
6
+ import { readFileSync } from "node:fs";
7
+ const toolSchemas = createToolSchemas();
8
+ export function createToolHandlers() {
9
+ const aseprite_check_environment = async () => {
10
+ try {
11
+ const asepritePath = await resolveAsepritePath();
12
+ const { stdout: version } = await runAsepriteCommand(["--version"]);
13
+ return successResult("aseprite_check_environment", { path: asepritePath, version });
14
+ }
15
+ catch (e) {
16
+ return errorResult("aseprite_check_environment", e instanceof Error ? e.message : String(e));
17
+ }
18
+ };
19
+ const aseprite_export_sheet = async ({ inputFile, outputSheet, sheetType = "packed", dataFile, tag }) => {
20
+ try {
21
+ const inputAbsPath = ensureSafePath(inputFile, { mustExist: true });
22
+ const sheetAbsPath = ensureSafePath(outputSheet, { createDirIfNeeded: true });
23
+ const dataAbsPath = dataFile ? ensureSafePath(dataFile, { createDirIfNeeded: true }) : undefined;
24
+ const args = [
25
+ "--batch",
26
+ `"${inputAbsPath}"`,
27
+ "--sheet",
28
+ `"${sheetAbsPath}"`,
29
+ "--sheet-type",
30
+ sheetType
31
+ ];
32
+ if (tag)
33
+ args.push("--tag", `"${tag}"`);
34
+ if (dataAbsPath)
35
+ args.push("--data", `"${dataAbsPath}"`);
36
+ const result = await runAsepriteCommand(args);
37
+ return successResult("aseprite_export_sheet", {
38
+ command: result.command,
39
+ inputFile: inputAbsPath,
40
+ outputSheet: sheetAbsPath,
41
+ sheetType,
42
+ dataFile: dataAbsPath ? dataAbsPath : undefined,
43
+ tag: tag ? tag : undefined,
44
+ stdout: result.stdout.trim(),
45
+ stderr: result.stderr.trim(),
46
+ });
47
+ }
48
+ catch (e) {
49
+ return errorResult("aseprite_export_sheet", e instanceof Error ? e.message : String(e));
50
+ }
51
+ };
52
+ const aseprite_export_frames = async ({ inputFile, outputPattern, tag }) => {
53
+ try {
54
+ const inputAbsPath = ensureSafePath(inputFile, { mustExist: true });
55
+ const outputAbsPath = ensureSafePath(outputPattern, { createDirIfNeeded: true });
56
+ const args = [
57
+ "--batch",
58
+ `"${inputAbsPath}"`,
59
+ "--save-as",
60
+ `"${outputAbsPath}"`
61
+ ];
62
+ if (tag)
63
+ args.push("--tag", `"${tag}"`);
64
+ const result = await runAsepriteCommand(args);
65
+ return successResult("aseprite_export_frames", {
66
+ command: result.command,
67
+ inputFile: inputAbsPath,
68
+ outputPattern: outputAbsPath,
69
+ tag: tag ? tag : undefined,
70
+ stdout: result.stdout.trim(),
71
+ stderr: result.stderr.trim(),
72
+ });
73
+ }
74
+ catch (e) {
75
+ return errorResult("aseprite_export_frames", e instanceof Error ? e.message : String(e));
76
+ }
77
+ };
78
+ const aseprite_export_metadata = async ({ inputFile, dataFile, format }) => {
79
+ try {
80
+ const inputAbsPath = ensureSafePath(inputFile, { mustExist: true });
81
+ const dataAbsPath = ensureSafePath(dataFile, { createDirIfNeeded: true });
82
+ const args = [
83
+ "--batch",
84
+ `"${inputAbsPath}"`,
85
+ "--data",
86
+ `"${dataAbsPath}"`
87
+ ];
88
+ if (format)
89
+ args.push("--format", `"${format}"`);
90
+ const result = await runAsepriteCommand(args);
91
+ let metaText = "";
92
+ try {
93
+ metaText = readFileSync(dataAbsPath, "utf8");
94
+ }
95
+ catch (e) {
96
+ metaText = `Failed to read metadata: ${e instanceof Error ? e.message : String(e)}`;
97
+ }
98
+ return successResult("aseprite_export_metadata", {
99
+ command: result.command,
100
+ inputFile: inputAbsPath,
101
+ dataFile: dataAbsPath,
102
+ format: format ? format : undefined,
103
+ stdout: result.stdout.trim(),
104
+ stderr: result.stderr.trim(),
105
+ metadata: metaText,
106
+ });
107
+ }
108
+ catch (e) {
109
+ return errorResult("aseprite_export_metadata", e instanceof Error ? e.message : String(e));
110
+ }
111
+ };
112
+ return {
113
+ aseprite_check_environment,
114
+ aseprite_export_sheet,
115
+ aseprite_export_frames,
116
+ aseprite_export_metadata
117
+ };
118
+ }
119
+ export function createToolSchemas() {
120
+ return {
121
+ aseprite_export_sheet: z.object({
122
+ inputFile: z.string(),
123
+ outputSheet: z.string(),
124
+ sheetType: z.enum(["rows", "columns", "packed"]).optional().default("packed"),
125
+ dataFile: z.string().optional(),
126
+ tag: z.string().optional(),
127
+ }),
128
+ aseprite_export_frames: z.object({
129
+ inputFile: z.string(),
130
+ outputPattern: z.string(),
131
+ tag: z.string().optional(),
132
+ }),
133
+ aseprite_export_metadata: z.object({
134
+ inputFile: z.string(),
135
+ dataFile: z.string(),
136
+ format: z.string().optional(),
137
+ }),
138
+ aseprite_output_result: z.object({
139
+ content: z.array(z.object({
140
+ type: z.literal("text"),
141
+ text: z.string(),
142
+ })),
143
+ }),
144
+ };
145
+ }
package/build/util.js ADDED
@@ -0,0 +1,28 @@
1
+ export function successResult(tool, result) {
2
+ return {
3
+ content: [
4
+ {
5
+ type: "text",
6
+ text: JSON.stringify({
7
+ success: true,
8
+ tool,
9
+ result,
10
+ }, null, 2)
11
+ }
12
+ ]
13
+ };
14
+ }
15
+ export function errorResult(tool, error) {
16
+ return {
17
+ content: [
18
+ {
19
+ type: "text",
20
+ text: JSON.stringify({
21
+ success: false,
22
+ tool,
23
+ error,
24
+ }, null, 2)
25
+ }
26
+ ]
27
+ };
28
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@iborymagic/aseprite-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for using Aseprite API",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "aseprite-mcp": "./build/index.js"
9
+ },
10
+ "files": [
11
+ "build"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "start": "node build/index.js",
16
+ "dev": "npm run build && npm run start",
17
+ "test": "jest --runInBand --slient=false"
18
+ },
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.25.1",
21
+ "zod": "^4.2.1"
22
+ },
23
+ "devDependencies": {
24
+ "@types/jest": "^30.0.0",
25
+ "@types/node": "^25.0.0",
26
+ "jest": "^30.2.0",
27
+ "ts-jest": "^29.4.6",
28
+ "typescript": "^5.0.0"
29
+ },
30
+ "author": "iborymagic",
31
+ "license": "MIT"
32
+ }