document-tools.ts•10.2 kB
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { sendCommandToFigma, joinChannel } from "../utils/websocket.js";
import { filterFigmaNode } from "../utils/figma-helpers.js";
/**
* Register document-related tools to the MCP server
* @param server - The MCP server instance
*/
export function registerDocumentTools(server: McpServer): void {
// Document Info Tool
server.tool(
"get_document_info",
"Get detailed information about the current Figma document",
{},
async () => {
try {
const result = await sendCommandToFigma("get_document_info");
return {
content: [
{
type: "text",
text: JSON.stringify(result)
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error getting document info: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
);
// Selection Tool
server.tool(
"get_selection",
"Get information about the current selection in Figma",
{},
async () => {
try {
const result = await sendCommandToFigma("get_selection");
return {
content: [
{
type: "text",
text: JSON.stringify(result)
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error getting selection: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
);
// Node Info Tool
server.tool(
"get_node_info",
"Get detailed information about a specific node in Figma",
{
nodeId: z.string().describe("The ID of the node to get information about"),
},
async ({ nodeId }) => {
try {
const result = await sendCommandToFigma("get_node_info", { nodeId });
return {
content: [
{
type: "text",
text: JSON.stringify(filterFigmaNode(result))
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error getting node info: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
);
// Nodes Info Tool
server.tool(
"get_nodes_info",
"Get detailed information about multiple nodes in Figma",
{
nodeIds: z.array(z.string()).describe("Array of node IDs to get information about")
},
async ({ nodeIds }) => {
try {
const results = await Promise.all(
nodeIds.map(async (nodeId) => {
const result = await sendCommandToFigma('get_node_info', { nodeId });
return { nodeId, info: result };
})
);
return {
content: [
{
type: "text",
text: JSON.stringify(results.map((result) => filterFigmaNode(result.info)))
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error getting nodes info: ${error instanceof Error ? error.message : String(error)}`
}
]
};
}
}
);
// Get Styles Tool
server.tool(
"get_styles",
"Get all styles from the current Figma document",
{},
async () => {
try {
const result = await sendCommandToFigma("get_styles");
return {
content: [
{
type: "text",
text: JSON.stringify(result)
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error getting styles: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
);
// Get Local Components Tool
server.tool(
"get_local_components",
"Get all local components from the Figma document",
{},
async () => {
try {
const result = await sendCommandToFigma("get_local_components");
return {
content: [
{
type: "text",
text: JSON.stringify(result)
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error getting local components: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
);
// Get Remote Components Tool
server.tool(
"get_remote_components",
"Get available components from team libraries in Figma",
{},
async () => {
try {
const result = await sendCommandToFigma("get_remote_components");
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error getting remote components: ${error instanceof Error ? error.message : String(error)}`
}
]
};
}
}
);
// Text Node Scanning Tool
server.tool(
"scan_text_nodes",
"Scan all text nodes in the selected Figma node",
{
nodeId: z.string().describe("ID of the node to scan"),
},
async ({ nodeId }) => {
try {
// Initial response to indicate we're starting the process
const initialStatus = {
type: "text" as const,
text: "Starting text node scanning. This may take a moment for large designs...",
};
// Use the plugin's scan_text_nodes function with chunking flag
const result = await sendCommandToFigma("scan_text_nodes", {
nodeId,
useChunking: true, // Enable chunking on the plugin side
chunkSize: 10 // Process 10 nodes at a time
});
// If the result indicates chunking was used, format the response accordingly
if (result && typeof result === 'object' && 'chunks' in result) {
const typedResult = result as {
success: boolean,
totalNodes: number,
processedNodes: number,
chunks: number,
textNodes: Array<any>
};
const summaryText = `
Scan completed:
- Found ${typedResult.totalNodes} text nodes
- Processed in ${typedResult.chunks} chunks
`;
return {
content: [
initialStatus,
{
type: "text" as const,
text: summaryText
},
{
type: "text" as const,
text: JSON.stringify(typedResult.textNodes, null, 2)
}
],
};
}
// If chunking wasn't used or wasn't reported in the result format, return the result as is
return {
content: [
initialStatus,
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error scanning text nodes: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
);
// Join Channel Tool
server.tool(
"join_channel",
"Join a specific channel to communicate with Figma",
{
channel: z.string().describe("The name of the channel to join").default(""),
},
async ({ channel }) => {
try {
if (!channel) {
// If no channel provided, ask the user for input
return {
content: [
{
type: "text",
text: "Please provide a channel name to join:",
},
],
followUp: {
tool: "join_channel",
description: "Join the specified channel",
},
};
}
// Use joinChannel instead of sendCommandToFigma to ensure currentChannel is updated
await joinChannel(channel);
return {
content: [
{
type: "text",
text: `Successfully joined channel: ${channel}`,
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error joining channel: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
);
// Export Node as Image Tool
server.tool(
"export_node_as_image",
"Export a node as an image from Figma",
{
nodeId: z.string().describe("The ID of the node to export"),
format: z
.enum(["PNG", "JPG", "SVG", "PDF"])
.optional()
.describe("Export format"),
scale: z.number().positive().optional().describe("Export scale"),
},
async ({ nodeId, format, scale }) => {
try {
const result = await sendCommandToFigma("export_node_as_image", {
nodeId,
format: format || "PNG",
scale: scale || 1,
});
const typedResult = result as { imageData: string; mimeType: string };
return {
content: [
{
type: "image",
data: typedResult.imageData,
mimeType: typedResult.mimeType || "image/png",
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error exporting node as image: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
);
}