@genwave/svgmaker-mcp
Version:
MCP server for generating, editing, and converting SVG images using SVGMaker API
287 lines • 12.2 kB
JavaScript
import fs from 'fs/promises';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import * as svgmakerService from '../services/svgmakerService.js';
import * as fileUtils from '../utils/fileUtils.js';
import { logToFile } from '../utils/logUtils.js';
import { fetchRemotePreview } from '../utils/previewUtils.js';
// ── Generations List Tool ──
const GenerationsListToolInputSchema = z.object({
page: z.number().int().positive().optional().describe('Page number (1-indexed)'),
limit: z.number().int().min(1).max(100).optional().describe('Items per page (1-100)'),
type: z
.string()
.optional()
.describe('Filter by generation type (e.g., "generate", "edit", "convert")'),
hashtags: z.array(z.string()).optional().describe('Filter by hashtags'),
categories: z.array(z.string()).optional().describe('Filter by categories'),
query: z.string().optional().describe('Search query for prompt/description'),
});
export const generationsListToolDefinition = {
name: 'svgmaker_generations_list',
description: 'Lists your SVG generations with optional filtering and pagination. Returns generation IDs that can be used with other generation tools.',
inputSchema: zodToJsonSchema(GenerationsListToolInputSchema),
};
export async function handleGenerationsListTool(server, request) {
logToFile('========== SVG GENERATIONS LIST TOOL CALLED ==========');
logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`);
try {
const validatedArgs = GenerationsListToolInputSchema.parse(request.params.arguments);
// Build params with only defined fields
const params = {};
if (validatedArgs.page !== undefined)
params.page = validatedArgs.page;
if (validatedArgs.limit !== undefined)
params.limit = validatedArgs.limit;
if (validatedArgs.type !== undefined)
params.type = validatedArgs.type;
if (validatedArgs.hashtags !== undefined)
params.hashtags = validatedArgs.hashtags;
if (validatedArgs.categories !== undefined)
params.categories = validatedArgs.categories;
if (validatedArgs.query !== undefined)
params.query = validatedArgs.query;
const result = await svgmakerService.listGenerations(Object.keys(params).length > 0 ? params : undefined);
const lines = [];
const p = result.pagination;
lines.push(`Generations (Page ${p.page} of ${p.totalPages}, Total: ${p.totalItems}):`);
if (result.items && result.items.length > 0) {
for (const id of result.items) {
lines.push(`- ${id}`);
}
}
lines.push('');
lines.push(`Has Next Page: ${p.hasNextPage}`);
const text = lines.join('\n');
return {
content: [{ type: 'text', text }],
};
}
catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error listing generations: ${error.message}`,
},
],
};
}
}
// ── Generations Get Tool ──
const GenerationsGetToolInputSchema = z.object({
generation_id: z.string().min(1, 'Generation ID is required'),
});
export const generationsGetToolDefinition = {
name: 'svgmaker_generations_get',
description: 'Gets detailed information about a specific generation by ID, including the prompt used, type, quality level, visibility status, hashtags, and categories.',
inputSchema: zodToJsonSchema(GenerationsGetToolInputSchema),
};
export async function handleGenerationsGetTool(server, request) {
logToFile('========== SVG GENERATIONS GET TOOL CALLED ==========');
logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`);
try {
const validatedArgs = GenerationsGetToolInputSchema.parse(request.params.arguments);
const result = await svgmakerService.getGeneration(validatedArgs.generation_id);
const hashTags = result.hashTags && result.hashTags.length > 0 ? result.hashTags.join(', ') : 'None';
const categories = result.categories && result.categories.length > 0 ? result.categories.join(', ') : 'None';
const text = [
'Generation Details:',
`- ID: ${result.id}`,
`- Prompt: ${result.prompt}`,
`- Type: ${result.type}`,
`- Quality: ${result.quality}`,
`- Public: ${result.isPublic}`,
`- Hashtags: ${hashTags}`,
`- Categories: ${categories}`,
].join('\n');
return {
content: [{ type: 'text', text }],
};
}
catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error getting generation: ${error.message}`,
},
],
};
}
}
// ── Generations Delete Tool ──
const GenerationsDeleteToolInputSchema = z.object({
generation_id: z.string().min(1, 'Generation ID is required'),
});
export const generationsDeleteToolDefinition = {
name: 'svgmaker_generations_delete',
description: 'Deletes a generation and its associated files. This action is permanent and cannot be undone. Requires a paid account.',
inputSchema: zodToJsonSchema(GenerationsDeleteToolInputSchema),
};
export async function handleGenerationsDeleteTool(server, request) {
logToFile('========== SVG GENERATIONS DELETE TOOL CALLED ==========');
logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`);
try {
const validatedArgs = GenerationsDeleteToolInputSchema.parse(request.params.arguments);
const result = await svgmakerService.deleteGeneration(validatedArgs.generation_id);
return {
content: [{ type: 'text', text: result.message }],
};
}
catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error deleting generation: ${error.message}`,
},
],
};
}
}
// ── Generations Share Tool ──
const GenerationsShareToolInputSchema = z.object({
generation_id: z.string().min(1, 'Generation ID is required'),
});
export const generationsShareToolDefinition = {
name: 'svgmaker_generations_share',
description: 'Shares a generation by making it publicly accessible. Returns the public share URL.',
inputSchema: zodToJsonSchema(GenerationsShareToolInputSchema),
};
export async function handleGenerationsShareTool(server, request) {
logToFile('========== SVG GENERATIONS SHARE TOOL CALLED ==========');
logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`);
try {
const validatedArgs = GenerationsShareToolInputSchema.parse(request.params.arguments);
const result = await svgmakerService.shareGeneration(validatedArgs.generation_id);
const text = [
'Generation shared successfully.',
`- Public: ${result.isPublic}`,
`- Share URL: ${result.shareUrl}`,
].join('\n');
return {
content: [{ type: 'text', text }],
};
}
catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error sharing generation: ${error.message}`,
},
],
};
}
}
// ── Generations Download Tool ──
const GenerationsDownloadToolInputSchema = z.object({
generation_id: z.string().min(1, 'Generation ID is required'),
output_path: z
.string()
.min(1, 'Output path cannot be empty.')
.describe('Absolute file path where the downloaded file will be saved.'),
format: z
.enum(['svg', 'webp', 'png', 'svg-optimized', 'svgz'])
.optional()
.describe('Download format. Defaults to webp.'),
optimize: z.boolean().optional().describe('Optimize before compressing (only for svgz format).'),
});
export const generationsDownloadToolDefinition = {
name: 'svgmaker_generations_download',
description: 'Downloads a generation in the specified format and saves it to a local file. Supports SVG, WebP, PNG, optimized SVG, and SVGZ formats. Requires a paid account.',
inputSchema: zodToJsonSchema(GenerationsDownloadToolInputSchema),
};
export async function handleGenerationsDownloadTool(server, request) {
logToFile('========== SVG GENERATIONS DOWNLOAD TOOL CALLED ==========');
logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`);
try {
const validatedArgs = GenerationsDownloadToolInputSchema.parse(request.params.arguments);
const validatedOutputPath = await fileUtils.resolveAndValidatePath(validatedArgs.output_path, [], 'write');
// Build SDK params with only defined fields
const params = {};
if (validatedArgs.format !== undefined)
params.format = validatedArgs.format;
if (validatedArgs.optimize !== undefined)
params.optimize = validatedArgs.optimize;
const result = await svgmakerService.downloadGeneration(validatedArgs.generation_id, Object.keys(params).length > 0 ? params : undefined);
// Fetch the file from the download URL
const response = await fetch(result.url);
if (!response.ok) {
throw new Error(`Failed to download file: HTTP ${response.status}`);
}
// Determine if text or binary based on format
const isTextFormat = validatedArgs.format === 'svg' || validatedArgs.format === 'svg-optimized';
if (isTextFormat) {
await fileUtils.writeFile(validatedOutputPath, await response.text());
}
else {
await fs.writeFile(validatedOutputPath, Buffer.from(await response.arrayBuffer()));
}
return {
content: [
{
type: 'text',
text: `File downloaded successfully: ${validatedOutputPath} (format: ${result.format})`,
},
],
};
}
catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error downloading generation: ${error.message}`,
},
],
};
}
}
// ── Generations Preview Tool ──
const GenerationsPreviewToolInputSchema = z.object({
generation_id: z.string().min(1, 'Generation ID is required'),
});
export const generationsPreviewToolDefinition = {
name: 'svgmaker_generations_preview',
description: 'Previews a generation by returning the image directly in the chat context as a PNG image. Use this to visually inspect a generation without saving it to disk.',
inputSchema: zodToJsonSchema(GenerationsPreviewToolInputSchema),
};
export async function handleGenerationsPreviewTool(server, request) {
logToFile('========== SVG GENERATIONS PREVIEW TOOL CALLED ==========');
logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`);
try {
const validatedArgs = GenerationsPreviewToolInputSchema.parse(request.params.arguments);
const result = await svgmakerService.downloadGeneration(validatedArgs.generation_id, {
format: 'png',
});
const base64Data = await fetchRemotePreview(result.url);
return {
content: [
{
type: 'image',
data: base64Data,
mimeType: 'image/png',
},
],
};
}
catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error previewing generation: ${error.message}`,
},
],
};
}
}
//# sourceMappingURL=generationsTool.js.map