spex-mcp
Version:
MCP server for Figma SpeX plugin and Cursor AI integration
299 lines (275 loc) • 9.23 kB
JavaScript
/**
* MCPToolsManager handles all MCP tools for Cursor AI integration
* Supports image injection as per Cursor MCP documentation
*/
export class MCPToolsManager {
constructor(figmaManager) {
this.figmaManager = figmaManager;
}
/**
* Get list of all available MCP tools for Cursor AI
*/
getToolsList() {
return [
{
name: "hello-world",
description: "Returns a simple hello world message",
inputSchema: {
type: "object",
properties: {},
required: [],
},
},
{
name: "get-specs-readme",
description: "Fetches the README.md file from the SpeX plugin that contains documentation about the exported design specs",
inputSchema: {
type: "object",
properties: {},
required: [],
},
},
{
name: "get-spec-files-manifest",
description: "Fetches the manifest.yaml file from the SpeX plugin to confirm design specs are ready and get the list of available spec files",
inputSchema: {
type: "object",
properties: {},
required: [],
},
},
{
name: "get-a-spec-file",
description: "Fetches a specific design specification file from the SpeX plugin by filename",
inputSchema: {
type: "object",
properties: {
file_name: {
type: "string",
description: "The name of the spec file to retrieve (e.g., 'screen.yaml', 'components/button.yaml')"
}
},
required: ["file_name"],
},
},
{
name: "get-page-thumbnail",
description: "Fetches a thumbnail image of the current page or screen from the Figma design",
inputSchema: {
type: "object",
properties: {},
required: [],
},
},
];
}
/**
* Handle MCP tool calls from Cursor AI
*/
async handleToolCall(name, args = {}) {
switch (name) {
case "hello-world":
return this.handleHelloWorld();
case "get-specs-readme":
return this.handleGetSpecsReadme();
case "get-spec-files-manifest":
return this.handleGetSpecFilesManifest();
case "get-a-spec-file":
return this.handleGetASpecFile(args);
case "get-page-thumbnail":
return this.handleGetPageThumbnail(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
}
/**
* Handle hello world tool call
*/
handleHelloWorld() {
return {
content: [
{
type: "text",
text: "Hello World from SpeX MCP Server! 🎨",
},
],
};
}
/**
* Handle get specs readme tool call
*/
async handleGetSpecsReadme() {
try {
const result = await this.figmaManager.requestSpecFile('get-specs-readme');
if (result.success && result.data) {
return {
content: [
{
type: "text",
text: result.data.content,
},
],
};
} else {
return {
content: [
{
type: "text",
text: `Error fetching README: ${result.error || 'Unknown error'}`,
},
],
};
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error requesting README: ${error.message}`,
},
],
};
}
}
/**
* Handle get spec files manifest tool call
*/
async handleGetSpecFilesManifest() {
try {
const result = await this.figmaManager.requestSpecFile('get-spec-files-manifest');
if (result.success && result.data) {
return {
content: [
{
type: "text",
text: result.data.content,
},
],
};
} else {
return {
content: [
{
type: "text",
text: `Error fetching manifest: ${result.error || 'Unknown error'}`,
},
],
};
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error requesting manifest: ${error.message}`,
},
],
};
}
}
/**
* Handle get a spec file tool call
*/
async handleGetASpecFile(args) {
try {
const { file_name } = args;
if (!file_name) {
return {
content: [
{
type: "text",
text: "Error: file_name parameter is required",
},
],
};
}
const result = await this.figmaManager.requestSpecFile('get-a-spec-file', file_name);
if (result.success && result.data) {
return {
content: [
{
type: "text",
text: result.data.content,
},
],
};
} else {
return {
content: [
{
type: "text",
text: `Error fetching file '${file_name}': ${result.error || 'Unknown error'}`,
},
],
};
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error requesting file: ${error.message}`,
},
],
};
}
}
/**
* Handle get component screenshot tool call
* Returns visual screenshot of a component for reference
*/
/**
* Handle get page thumbnail tool call
* Returns thumbnail image of the current page/screen from plugin's preview files
*/
async handleGetPageThumbnail(args) {
try {
const result = await this.figmaManager.requestSpecFile('get-page-thumbnail');
if (result.success && result.data) {
// Check if response contains image data
if (result.data.image_data && result.data.mime_type) {
return {
content: [
{
type: "text",
text: "Page thumbnail from Figma design:",
},
{
type: "image",
data: result.data.image_data,
mimeType: result.data.mime_type,
},
],
};
} else {
return {
content: [
{
type: "text",
text: result.data.content || "Page thumbnail is not available as image data",
},
],
};
}
} else {
return {
content: [
{
type: "text",
text: `Error fetching page thumbnail: ${result.error || 'Unknown error'}`,
},
],
};
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error requesting page thumbnail: ${error.message}`,
},
],
};
}
}
}