UNPKG

pixellab-mcp

Version:

MCP server for PixelLab pixel art generation and manipulation. Usage: pixellab-mcp --secret=your-key

108 lines (107 loc) 4.52 kB
import { z } from "zod"; import { Base64Image } from "@pixellab-code/pixellab"; import { toMcpError } from "../utils.js"; const AnimateWithSkeletonParamsSchema = z.object({ skeleton_frames: z.array(z.object({ keypoints: z.array(z.object({ x: z.number(), y: z.number(), label: z.enum([ "NOSE", "NECK", "RIGHT SHOULDER", "RIGHT ELBOW", "RIGHT ARM", "LEFT SHOULDER", "LEFT ELBOW", "LEFT ARM", "RIGHT HIP", "RIGHT KNEE", "RIGHT LEG", "LEFT HIP", "LEFT KNEE", "LEFT LEG", "RIGHT EYE", "LEFT EYE", "RIGHT EAR", "LEFT EAR" ]), z_index: z.number().optional().default(0.0), })), })), reference_image_path: z.string().optional(), width: z.number().min(16).max(512).default(64), height: z.number().min(16).max(512).default(64), view: z.enum(["side", "low top-down", "high top-down"]).default("side"), direction: z.enum([ "south", "south-east", "east", "north-east", "north", "north-west", "west", "south-west" ]).default("east"), reference_guidance_scale: z.number().min(1.0).max(20.0).default(1.1), pose_guidance_scale: z.number().min(1.0).max(20.0).default(3.0), isometric: z.boolean().default(false), oblique_projection: z.boolean().default(false), init_image_strength: z.number().min(0).max(1000).default(300), seed: z.number().optional().default(0), save_to_file: z.string().optional(), show_image: z.boolean().default(false), }); export async function animateWithSkeleton(args, client) { try { const params = AnimateWithSkeletonParamsSchema.parse(args); // Convert skeleton frames to the expected format const skeletonKeypoints = params.skeleton_frames.map(frame => ({ keypoints: frame.keypoints.map(kp => ({ x: kp.x, y: kp.y, label: kp.label, zIndex: kp.z_index, })) })); // Load reference image if provided let referenceImage; if (params.reference_image_path) { referenceImage = await Base64Image.fromFile(params.reference_image_path); } const imageSize = { width: params.width, height: params.height }; const result = await client.animateWithSkeleton({ imageSize, skeletonKeypoints, view: params.view, direction: params.direction, referenceGuidanceScale: params.reference_guidance_scale, poseGuidanceScale: params.pose_guidance_scale, isometric: params.isometric, obliqueProjection: params.oblique_projection, referenceImage, initImageStrength: params.init_image_strength, seed: params.seed, }); const contents = []; if (result.images.length > 0) { // Save files if requested if (params.save_to_file) { for (let i = 0; i < result.images.length; i++) { const fileName = params.save_to_file.replace(/(\.[^.]+)?$/, `_frame${i}$1`); await result.images[i].saveToFile(fileName); } contents.push({ type: "text", text: `Animated pixel art sequence with ${result.images.length} frames using skeleton keypoints. Cost: $${result.usage.usd.toFixed(4)}. Files saved: ${params.save_to_file.replace(/(\.[^.]+)?$/, "_frame{0-" + (result.images.length - 1) + "}$1")}`, }); } else { contents.push({ type: "text", text: `Animated pixel art sequence with ${result.images.length} frames using skeleton keypoints. Cost: $${result.usage.usd.toFixed(4)}`, }); } // Show images if requested if (params.show_image) { for (let i = 0; i < result.images.length; i++) { contents.push({ type: "image", data: result.images[i].base64, mimeType: "image/png", }); } } } else { contents.push({ type: "text", text: "No animated frames were generated.", }); } return { content: contents }; } catch (error) { return toMcpError(error); } }