UNPKG

@genwave/svgmaker-mcp

Version:

MCP server for generating, editing, and converting SVG images using SVGMaker API

226 lines 9.91 kB
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'; // ── Gallery List Tool ── const GalleryListToolInputSchema = 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'), pro: z.string().optional().describe('Filter for pro images. Pass "true" to filter.'), gold: z.string().optional().describe('Filter for gold images. Pass "true" to filter.'), }); export const galleryListToolDefinition = { name: 'svgmaker_gallery_list', description: 'Browse the public SVGMaker gallery with optional filtering and pagination. Returns gallery item IDs that can be used with other gallery tools.', inputSchema: zodToJsonSchema(GalleryListToolInputSchema), }; export async function handleGalleryListTool(server, request) { logToFile('========== SVG GALLERY LIST TOOL CALLED =========='); logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`); try { const validatedArgs = GalleryListToolInputSchema.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; if (validatedArgs.pro !== undefined) params.pro = validatedArgs.pro; if (validatedArgs.gold !== undefined) params.gold = validatedArgs.gold; const result = await svgmakerService.listGallery(Object.keys(params).length > 0 ? params : undefined); const lines = []; const p = result.pagination; lines.push(`Gallery (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 gallery: ${error.message}`, }, ], }; } } // ── Gallery Get Tool ── const GalleryGetToolInputSchema = z.object({ generation_id: z.string().min(1, 'Generation ID is required'), }); export const galleryGetToolDefinition = { name: 'svgmaker_gallery_get', description: 'Gets detailed information about a specific gallery item by ID, including the prompt used, type, quality level, hashtags, and categories.', inputSchema: zodToJsonSchema(GalleryGetToolInputSchema), }; export async function handleGalleryGetTool(server, request) { logToFile('========== SVG GALLERY GET TOOL CALLED =========='); logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`); try { const validatedArgs = GalleryGetToolInputSchema.parse(request.params.arguments); const result = await svgmakerService.getGalleryItem(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 = [ 'Gallery Item 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 gallery item: ${error.message}`, }, ], }; } } // ── Gallery Download Tool ── const GalleryDownloadToolInputSchema = 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 galleryDownloadToolDefinition = { name: 'svgmaker_gallery_download', description: 'Downloads a gallery item in the specified format and saves it to a local file. Supports SVG, WebP, PNG, optimized SVG, and SVGZ formats. Costs 1 credit for SVG formats (svg, svg-optimized, svgz), 0 credits for WebP/PNG.', inputSchema: zodToJsonSchema(GalleryDownloadToolInputSchema), }; export async function handleGalleryDownloadTool(server, request) { logToFile('========== SVG GALLERY DOWNLOAD TOOL CALLED =========='); logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`); try { const validatedArgs = GalleryDownloadToolInputSchema.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.downloadGalleryItem(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})\nNote: SVG formats cost 1 credit per download. WebP/PNG are free.`, }, ], }; } catch (error) { return { isError: true, content: [ { type: 'text', text: `Error downloading gallery item: ${error.message}`, }, ], }; } } // ── Gallery Preview Tool ── const GalleryPreviewToolInputSchema = z.object({ generation_id: z.string().min(1, 'Generation ID is required'), }); export const galleryPreviewToolDefinition = { name: 'svgmaker_gallery_preview', description: 'Previews a gallery item by returning the image directly in the chat context as a PNG image. Use this to visually inspect a gallery item without saving it to disk.', inputSchema: zodToJsonSchema(GalleryPreviewToolInputSchema), }; export async function handleGalleryPreviewTool(server, request) { logToFile('========== SVG GALLERY PREVIEW TOOL CALLED =========='); logToFile(`Arguments: ${JSON.stringify(request.params.arguments, null, 2)}`); try { const validatedArgs = GalleryPreviewToolInputSchema.parse(request.params.arguments); const result = await svgmakerService.downloadGalleryItem(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 gallery item: ${error.message}`, }, ], }; } } //# sourceMappingURL=galleryTool.js.map