spex-mcp
Version:
MCP server for Figma SpeX plugin and Cursor AI integration
163 lines (143 loc) ⢠5.54 kB
JavaScript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { createRequire } from 'module';
import { FigmaPluginManager } from "./figma-functions.js";
import { MCPToolsManager } from "./mcp-tools.js";
// Read version from package.json
const require = createRequire(import.meta.url);
const packageJson = require('../../package.json');
/**
* Main SpeX MCP Server that orchestrates both Figma SpeX plugin communication
* and MCP tools for Cursor AI integration
*/
class SpeXMCPServer {
constructor(port = 8080, force = false) {
// Initialize managers
this.figmaManager = new FigmaPluginManager(port, force);
this.mcpToolsManager = new MCPToolsManager(this.figmaManager);
// Initialize MCP server
this.server = new Server(
{
name: packageJson.name,
version: packageJson.version,
},
{
capabilities: {
tools: {},
},
}
);
this.setupMCPHandlers();
}
/**
* Setup MCP request handlers for Cursor AI integration
*/
setupMCPHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: this.mcpToolsManager.getToolsList(),
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
return await this.mcpToolsManager.handleToolCall(name, args);
});
}
/**
* Start the server with both Figma SpeX WebSocket and MCP connections
*/
async run() {
try {
// Start WebSocket server for Figma SpeX plugins
await this.figmaManager.setupWebSocketServer();
console.error(`ā
Figma SpeX WebSocket server started on port ${this.figmaManager.port}`);
// Start MCP server for Cursor AI
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("ā
MCP server started for Cursor AI integration");
// Setup graceful shutdown
this.setupGracefulShutdown();
} catch (error) {
console.error("ā Error starting SpeX MCP Server:", error);
process.exit(1);
}
}
/**
* Setup graceful shutdown handlers
*/
setupGracefulShutdown() {
const shutdown = async () => {
console.error("\nš Shutting down SpeX MCP Server...");
try {
await this.figmaManager.shutdown();
console.error("ā
Server shutdown completed");
} catch (error) {
console.error("ā Error during shutdown:", error);
}
process.exit(0);
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
process.on('uncaughtException', (error) => {
console.error('ā Uncaught Exception:', error);
shutdown();
});
process.on('unhandledRejection', (reason, promise) => {
console.error('ā Unhandled Rejection at:', promise, 'reason:', reason);
shutdown();
});
}
}
// Parse command line arguments
function parseArgs() {
const args = process.argv.slice(2);
const config = { port: 8080, force: false };
console.error(`š§ Parsing arguments: ${JSON.stringify(args)}`);
for (let i = 0; i < args.length; i++) {
if (args[i] === '--port' && i + 1 < args.length) {
const port = parseInt(args[i + 1]);
if (isNaN(port) || port < 1 || port > 65535) {
console.error("ā Invalid port number. Must be between 1 and 65535.");
process.exit(1);
}
config.port = port;
i++; // Skip the next argument since it's the port value
} else if (args[i] === '--force') {
config.force = true;
console.error(`š§ Force option enabled`)
} else if (args[i] === '--version' || args[i] === '-v') {
console.error(`spex-mcp version ${packageJson.version}`);
process.exit(0);
} else if (args[i] === '--help' || args[i] === '-h') {
console.error(`
SpeX MCP Server - Figma SpeX plugin integration with Cursor AI
Usage: node src/local/index.js [options]
Options:
--port <number> WebSocket server port (default: 8080)
--force Force kill any process using the port before starting
--version, -v Show version number
--help, -h Show this help message
Examples:
node src/local/index.js --port 3000
node src/local/index.js --force
node src/local/index.js --port 3000 --force
node src/local/index.js --version
node src/local/index.js
`);
process.exit(0);
}
}
console.error(`š§ Final config: ${JSON.stringify(config)}`);
return config;
}
// Start the server
const config = parseArgs();
const server = new SpeXMCPServer(config.port, config.force);
server.run().catch((error) => {
console.error("ā Failed to start server:", error);
process.exit(1);
});