Skip to main content
Glama

Roblox Studio MCP Server

index.ts28.9 kB
#!/usr/bin/env node /** * Roblox Studio MCP Server * * This server provides Model Context Protocol (MCP) tools for interacting with Roblox Studio. * It allows AI assistants to access Studio data, scripts, and objects through a bridge plugin. * * Usage: * npx robloxstudio-mcp * * Or add to your MCP configuration: * "robloxstudio": { * "command": "npx", * "args": ["-y", "robloxstudio-mcp"] * } */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { createHttpServer } from './http-server.js'; import { RobloxStudioTools } from './tools/index.js'; import { BridgeService } from './bridge-service.js'; class RobloxStudioMCPServer { private server: Server; private tools: RobloxStudioTools; private bridge: BridgeService; constructor() { this.server = new Server( { name: 'robloxstudio-mcp', version: '1.6.0', }, { capabilities: { tools: {}, }, } ); this.bridge = new BridgeService(); this.tools = new RobloxStudioTools(this.bridge); this.setupToolHandlers(); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // File System Tools { name: 'get_file_tree', description: 'Get complete hierarchy of the Roblox Studio project with script types, models, and folders', inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Optional path to start from (defaults to workspace root)', default: '' } } } }, { name: 'search_files', description: 'Find files by name, type, or content patterns', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (name, type, or content pattern)' }, searchType: { type: 'string', enum: ['name', 'type', 'content'], description: 'Type of search to perform', default: 'name' } }, required: ['query'] } }, // Studio Context Tools { name: 'get_place_info', description: 'Get place ID, name, and game settings', inputSchema: { type: 'object', properties: {} } }, { name: 'get_services', description: 'Get available Roblox services and their children', inputSchema: { type: 'object', properties: { serviceName: { type: 'string', description: 'Optional specific service name to query' } } } }, { name: 'search_objects', description: 'Find instances by name, class, or properties', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, searchType: { type: 'string', enum: ['name', 'class', 'property'], description: 'Type of search to perform', default: 'name' }, propertyName: { type: 'string', description: 'Property name when searchType is "property"' } }, required: ['query'] } }, // Property & Instance Tools { name: 'get_instance_properties', description: 'Get all properties of a specific instance', inputSchema: { type: 'object', properties: { instancePath: { type: 'string', description: 'Path to the instance' } }, required: ['instancePath'] } }, { name: 'get_instance_children', description: 'Get child objects and their types', inputSchema: { type: 'object', properties: { instancePath: { type: 'string', description: 'Path to the parent instance' } }, required: ['instancePath'] } }, { name: 'search_by_property', description: 'Find objects with specific property values', inputSchema: { type: 'object', properties: { propertyName: { type: 'string', description: 'Name of the property to search' }, propertyValue: { type: 'string', description: 'Value to search for' } }, required: ['propertyName', 'propertyValue'] } }, { name: 'get_class_info', description: 'Get available properties/methods for Roblox classes', inputSchema: { type: 'object', properties: { className: { type: 'string', description: 'Roblox class name' } }, required: ['className'] } }, // Project Tools { name: 'get_project_structure', description: 'Get complete game hierarchy. IMPORTANT: Use maxDepth parameter (default: 3) to explore deeper levels of the hierarchy. Set higher values like 5-10 for comprehensive exploration', inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Optional path to start from (defaults to workspace root)', default: '' }, maxDepth: { type: 'number', description: 'Maximum depth to traverse (default: 3). RECOMMENDED: Use 5-10 for thorough exploration. Higher values provide more complete structure', default: 3 }, scriptsOnly: { type: 'boolean', description: 'Show only scripts and script containers', default: false } } } }, // Property Modification Tools { name: 'set_property', description: 'Set a property on any Roblox instance', inputSchema: { type: 'object', properties: { instancePath: { type: 'string', description: 'Path to the instance (e.g., "game.Workspace.Part")' }, propertyName: { type: 'string', description: 'Name of the property to set' }, propertyValue: { description: 'Value to set the property to (any type)' } }, required: ['instancePath', 'propertyName', 'propertyValue'] } }, { name: 'mass_set_property', description: 'Set the same property on multiple instances at once', inputSchema: { type: 'object', properties: { paths: { type: 'array', items: { type: 'string' }, description: 'Array of instance paths to modify' }, propertyName: { type: 'string', description: 'Name of the property to set' }, propertyValue: { description: 'Value to set the property to (any type)' } }, required: ['paths', 'propertyName', 'propertyValue'] } }, { name: 'mass_get_property', description: 'Get the same property from multiple instances at once', inputSchema: { type: 'object', properties: { paths: { type: 'array', items: { type: 'string' }, description: 'Array of instance paths to read from' }, propertyName: { type: 'string', description: 'Name of the property to get' } }, required: ['paths', 'propertyName'] } }, // Object Creation/Deletion Tools { name: 'create_object', description: 'Create a new Roblox object instance (basic, without properties)', inputSchema: { type: 'object', properties: { className: { type: 'string', description: 'Roblox class name (e.g., "Part", "Script", "Folder")' }, parent: { type: 'string', description: 'Path to the parent instance (e.g., "game.Workspace")' }, name: { type: 'string', description: 'Optional name for the new object' } }, required: ['className', 'parent'] } }, { name: 'create_object_with_properties', description: 'Create a new Roblox object instance with initial properties', inputSchema: { type: 'object', properties: { className: { type: 'string', description: 'Roblox class name (e.g., "Part", "Script", "Folder")' }, parent: { type: 'string', description: 'Path to the parent instance (e.g., "game.Workspace")' }, name: { type: 'string', description: 'Optional name for the new object' }, properties: { type: 'object', description: 'Properties to set on creation' } }, required: ['className', 'parent'] } }, { name: 'mass_create_objects', description: 'Create multiple objects at once (basic, without properties)', inputSchema: { type: 'object', properties: { objects: { type: 'array', items: { type: 'object', properties: { className: { type: 'string', description: 'Roblox class name' }, parent: { type: 'string', description: 'Path to the parent instance' }, name: { type: 'string', description: 'Optional name for the object' } }, required: ['className', 'parent'] }, description: 'Array of objects to create' } }, required: ['objects'] } }, { name: 'mass_create_objects_with_properties', description: 'Create multiple objects at once with initial properties', inputSchema: { type: 'object', properties: { objects: { type: 'array', items: { type: 'object', properties: { className: { type: 'string', description: 'Roblox class name' }, parent: { type: 'string', description: 'Path to the parent instance' }, name: { type: 'string', description: 'Optional name for the object' }, properties: { type: 'object', description: 'Properties to set on creation' } }, required: ['className', 'parent'] }, description: 'Array of objects to create with properties' } }, required: ['objects'] } }, { name: 'delete_object', description: 'Delete a Roblox object instance', inputSchema: { type: 'object', properties: { instancePath: { type: 'string', description: 'Path to the instance to delete' } }, required: ['instancePath'] } }, // Smart Duplication Tools { name: 'smart_duplicate', description: 'Smart duplication with automatic naming, positioning, and property variations', inputSchema: { type: 'object', properties: { instancePath: { type: 'string', description: 'Path to the instance to duplicate' }, count: { type: 'number', description: 'Number of duplicates to create' }, options: { type: 'object', properties: { namePattern: { type: 'string', description: 'Name pattern with {n} placeholder (e.g., "Button{n}")' }, positionOffset: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3, description: 'X, Y, Z offset per duplicate' }, rotationOffset: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3, description: 'X, Y, Z rotation offset per duplicate' }, scaleOffset: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3, description: 'X, Y, Z scale multiplier per duplicate' }, propertyVariations: { type: 'object', description: 'Property name to array of values' }, targetParents: { type: 'array', items: { type: 'string' }, description: 'Different parent for each duplicate' } } } }, required: ['instancePath', 'count'] } }, { name: 'mass_duplicate', description: 'Perform multiple smart duplications at once', inputSchema: { type: 'object', properties: { duplications: { type: 'array', items: { type: 'object', properties: { instancePath: { type: 'string', description: 'Path to the instance to duplicate' }, count: { type: 'number', description: 'Number of duplicates to create' }, options: { type: 'object', properties: { namePattern: { type: 'string', description: 'Name pattern with {n} placeholder' }, positionOffset: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3, description: 'X, Y, Z offset per duplicate' }, rotationOffset: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3, description: 'X, Y, Z rotation offset per duplicate' }, scaleOffset: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3, description: 'X, Y, Z scale multiplier per duplicate' }, propertyVariations: { type: 'object', description: 'Property name to array of values' }, targetParents: { type: 'array', items: { type: 'string' }, description: 'Different parent for each duplicate' } } } }, required: ['instancePath', 'count'] }, description: 'Array of duplication operations' } }, required: ['duplications'] } }, // Calculated Property Tools { name: 'set_calculated_property', description: 'Set properties using mathematical formulas and variables', inputSchema: { type: 'object', properties: { paths: { type: 'array', items: { type: 'string' }, description: 'Array of instance paths to modify' }, propertyName: { type: 'string', description: 'Name of the property to set' }, formula: { type: 'string', description: 'Mathematical formula (e.g., "Position.magnitude * 2", "index * 50")' }, variables: { type: 'object', description: 'Additional variables for the formula' } }, required: ['paths', 'propertyName', 'formula'] } }, // Relative Property Tools { name: 'set_relative_property', description: 'Modify properties relative to their current values', inputSchema: { type: 'object', properties: { paths: { type: 'array', items: { type: 'string' }, description: 'Array of instance paths to modify' }, propertyName: { type: 'string', description: 'Name of the property to modify' }, operation: { type: 'string', enum: ['add', 'multiply', 'divide', 'subtract', 'power'], description: 'Mathematical operation to perform' }, value: { description: 'Value to use in the operation' }, component: { type: 'string', enum: ['X', 'Y', 'Z'], description: 'Specific component for Vector3/UDim2 properties' } }, required: ['paths', 'propertyName', 'operation', 'value'] } }, // Script Management Tools { name: 'get_script_source', description: 'Get the source code of a script object (LocalScript, Script, or ModuleScript)', inputSchema: { type: 'object', properties: { instancePath: { type: 'string', description: 'Path to the script instance (e.g., "game.ServerScriptService.MainScript")' } }, required: ['instancePath'] } }, { name: 'set_script_source', description: 'Safely set the source code of a script object without using loadstring (Studio only)', inputSchema: { type: 'object', properties: { instancePath: { type: 'string', description: 'Path to the script instance (e.g., "game.ServerScriptService.MainScript")' }, source: { type: 'string', description: 'New source code for the script' } }, required: ['instancePath', 'source'] } } ] }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { // File System Tools case 'get_file_tree': return await this.tools.getFileTree((args as any)?.path || ''); case 'search_files': return await this.tools.searchFiles((args as any)?.query as string, (args as any)?.searchType || 'name'); // Studio Context Tools case 'get_place_info': return await this.tools.getPlaceInfo(); case 'get_services': return await this.tools.getServices((args as any)?.serviceName); case 'search_objects': return await this.tools.searchObjects((args as any)?.query as string, (args as any)?.searchType || 'name', (args as any)?.propertyName); // Property & Instance Tools case 'get_instance_properties': return await this.tools.getInstanceProperties((args as any)?.instancePath as string); case 'get_instance_children': return await this.tools.getInstanceChildren((args as any)?.instancePath as string); case 'search_by_property': return await this.tools.searchByProperty((args as any)?.propertyName as string, (args as any)?.propertyValue as string); case 'get_class_info': return await this.tools.getClassInfo((args as any)?.className as string); // Project Tools case 'get_project_structure': return await this.tools.getProjectStructure((args as any)?.path, (args as any)?.maxDepth, (args as any)?.scriptsOnly); // Property Modification Tools case 'set_property': return await this.tools.setProperty((args as any)?.instancePath as string, (args as any)?.propertyName as string, (args as any)?.propertyValue); // Mass Property Tools case 'mass_set_property': return await this.tools.massSetProperty((args as any)?.paths as string[], (args as any)?.propertyName as string, (args as any)?.propertyValue); case 'mass_get_property': return await this.tools.massGetProperty((args as any)?.paths as string[], (args as any)?.propertyName as string); // Object Creation/Deletion Tools case 'create_object': return await this.tools.createObject((args as any)?.className as string, (args as any)?.parent as string, (args as any)?.name); case 'create_object_with_properties': return await this.tools.createObjectWithProperties((args as any)?.className as string, (args as any)?.parent as string, (args as any)?.name, (args as any)?.properties); case 'mass_create_objects': return await this.tools.massCreateObjects((args as any)?.objects); case 'mass_create_objects_with_properties': return await this.tools.massCreateObjectsWithProperties((args as any)?.objects); case 'delete_object': return await this.tools.deleteObject((args as any)?.instancePath as string); // Smart Duplication Tools case 'smart_duplicate': return await this.tools.smartDuplicate((args as any)?.instancePath as string, (args as any)?.count as number, (args as any)?.options); case 'mass_duplicate': return await this.tools.massDuplicate((args as any)?.duplications); // Calculated Property Tools case 'set_calculated_property': return await this.tools.setCalculatedProperty((args as any)?.paths as string[], (args as any)?.propertyName as string, (args as any)?.formula as string, (args as any)?.variables); // Relative Property Tools case 'set_relative_property': return await this.tools.setRelativeProperty((args as any)?.paths as string[], (args as any)?.propertyName as string, (args as any)?.operation, (args as any)?.value, (args as any)?.component); // Script Management Tools case 'get_script_source': return await this.tools.getScriptSource((args as any)?.instancePath as string); case 'set_script_source': return await this.tools.setScriptSource((args as any)?.instancePath as string, (args as any)?.source as string); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } catch (error) { throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}` ); } }); } async run() { const port = process.env.ROBLOX_STUDIO_PORT ? parseInt(process.env.ROBLOX_STUDIO_PORT) : 3002; const host = process.env.ROBLOX_STUDIO_HOST || '0.0.0.0'; const httpServer = createHttpServer(this.tools, this.bridge); await new Promise<void>((resolve) => { httpServer.listen(port, host, () => { console.error(`HTTP server listening on ${host}:${port} for Studio plugin`); resolve(); }); }); const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Roblox Studio MCP server running on stdio'); (httpServer as any).setMCPServerActive(true); console.error('MCP server marked as active'); console.error('Waiting for Studio plugin to connect...'); setInterval(() => { const pluginConnected = (httpServer as any).isPluginConnected(); const mcpActive = (httpServer as any).isMCPServerActive(); if (pluginConnected && mcpActive) { } else if (pluginConnected && !mcpActive) { console.error('Studio plugin connected, but MCP server inactive'); } else if (!pluginConnected && mcpActive) { console.error('MCP server active, waiting for Studio plugin...'); } else { console.error('Waiting for connections...'); } }, 5000); setInterval(() => { this.bridge.cleanupOldRequests(); }, 5000); } } const server = new RobloxStudioMCPServer(); server.run().catch((error) => { console.error('Server failed to start:', error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/boshyxd/robloxstudio-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server