import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { sendCommandToFigma } from "../communication";
import { logger } from "../logger";
/**
* Register transform-related MCP tools (move, resize, delete, clone)
*/
export function registerTransformTools(server: McpServer): void {
// Move Node
server.tool(
"move_node",
"Move a node to a new position in Figma",
{
nodeId: z.string().describe("The ID of the node to move"),
x: z.number().describe("New X position"),
y: z.number().describe("New Y position"),
},
async ({ nodeId, x, y }) => {
try {
const result = await sendCommandToFigma<{ name: string }>("move_node", {
nodeId,
x,
y,
});
return {
content: [
{
type: "text",
text: `Moved node "${result.name}" to position (${x}, ${y})`,
},
],
};
} catch (error) {
logger.error("Error moving node", error);
return {
content: [
{
type: "text",
text: `Error moving node: ${
error instanceof Error ? error.message : String(error)
}`,
},
],
};
}
},
);
// Resize Node
server.tool(
"resize_node",
"Resize a node in Figma",
{
nodeId: z.string().describe("The ID of the node to resize"),
width: z.number().positive().describe("New width"),
height: z.number().positive().describe("New height"),
},
async ({ nodeId, width, height }) => {
try {
const result = await sendCommandToFigma<{ name: string }>(
"resize_node",
{ nodeId, width, height },
);
return {
content: [
{
type: "text",
text: `Resized node "${result.name}" to ${width}x${height}`,
},
],
};
} catch (error) {
logger.error("Error resizing node", error);
return {
content: [
{
type: "text",
text: `Error resizing node: ${
error instanceof Error ? error.message : String(error)
}`,
},
],
};
}
},
);
// Delete Node
server.tool(
"delete_node",
"Delete a node from Figma",
{
nodeId: z.string().describe("The ID of the node to delete"),
},
async ({ nodeId }) => {
try {
const result = await sendCommandToFigma<{ success: boolean }>(
"delete_node",
{ nodeId },
);
return {
content: [
{
type: "text",
text: result.success
? `Successfully deleted node ${nodeId}`
: `Failed to delete node ${nodeId}`,
},
],
};
} catch (error) {
logger.error("Error deleting node", error);
return {
content: [
{
type: "text",
text: `Error deleting node: ${
error instanceof Error ? error.message : String(error)
}`,
},
],
};
}
},
);
// Delete Multiple Nodes
server.tool(
"delete_multiple_nodes",
"Delete multiple nodes from Figma",
{
nodeIds: z.array(z.string()).describe("Array of node IDs to delete"),
},
async ({ nodeIds }) => {
try {
const result = await sendCommandToFigma<{
success: boolean;
deletedCount: number;
}>("delete_multiple_nodes", { nodeIds });
return {
content: [
{
type: "text",
text: `Deleted ${result.deletedCount} of ${nodeIds.length} nodes`,
},
],
};
} catch (error) {
logger.error("Error deleting multiple nodes", error);
return {
content: [
{
type: "text",
text: `Error deleting multiple nodes: ${
error instanceof Error ? error.message : String(error)
}`,
},
],
};
}
},
);
// Clone Node
server.tool(
"clone_node",
"Clone an existing node in Figma",
{
nodeId: z.string().describe("The ID of the node to clone"),
x: z.number().optional().describe("New X position for the clone"),
y: z.number().optional().describe("New Y position for the clone"),
},
async ({ nodeId, x, y }) => {
try {
const result = await sendCommandToFigma<{ name: string; id: string }>(
"clone_node",
{ nodeId, x, y },
);
const positionText =
x !== undefined && y !== undefined ? ` at position (${x}, ${y})` : "";
return {
content: [
{
type: "text",
text: `Cloned node "${result.name}" with new ID: ${result.id}${positionText}`,
},
],
};
} catch (error) {
logger.error("Error cloning node", error);
return {
content: [
{
type: "text",
text: `Error cloning node: ${
error instanceof Error ? error.message : String(error)
}`,
},
],
};
}
},
);
}