Skip to main content
Glama

Roblox Studio MCP Server

http-server.ts9.29 kB
import express from 'express'; import cors from 'cors'; import { RobloxStudioTools } from './tools/index.js'; import { BridgeService } from './bridge-service.js'; export function createHttpServer(tools: RobloxStudioTools, bridge: BridgeService) { const app = express(); let pluginConnected = false; let mcpServerActive = false; let lastMCPActivity = 0; let mcpServerStartTime = 0; let lastPluginActivity = 0; // Track MCP server lifecycle const setMCPServerActive = (active: boolean) => { mcpServerActive = active; if (active) { mcpServerStartTime = Date.now(); lastMCPActivity = Date.now(); } else { mcpServerStartTime = 0; lastMCPActivity = 0; } }; const trackMCPActivity = () => { if (mcpServerActive) { lastMCPActivity = Date.now(); } }; const isMCPServerActive = () => { return mcpServerActive && (Date.now() - lastMCPActivity < 15000); // 15 second timeout }; const isPluginConnected = () => { // Consider plugin disconnected if no activity for 10 seconds return pluginConnected && (Date.now() - lastPluginActivity < 10000); }; app.use(cors()); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb', extended: true })); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'ok', service: 'robloxstudio-mcp', pluginConnected, mcpServerActive: isMCPServerActive(), uptime: mcpServerActive ? Date.now() - mcpServerStartTime : 0 }); }); // Plugin readiness endpoint app.post('/ready', (req, res) => { pluginConnected = true; lastPluginActivity = Date.now(); res.json({ success: true }); }); // Plugin disconnect endpoint app.post('/disconnect', (req, res) => { pluginConnected = false; // Clear any pending requests when plugin disconnects bridge.clearAllPendingRequests(); res.json({ success: true }); }); // Enhanced status endpoint app.get('/status', (req, res) => { res.json({ pluginConnected, mcpServerActive: isMCPServerActive(), lastMCPActivity, uptime: mcpServerActive ? Date.now() - mcpServerStartTime : 0 }); }); // Enhanced polling endpoint for Studio plugin app.get('/poll', (req, res) => { // Always track that plugin is polling (shows it's trying to connect) if (!pluginConnected) { pluginConnected = true; } lastPluginActivity = Date.now(); if (!isMCPServerActive()) { res.status(503).json({ error: 'MCP server not connected', pluginConnected: true, mcpConnected: false, request: null }); return; } trackMCPActivity(); const pendingRequest = bridge.getPendingRequest(); if (pendingRequest) { res.json({ request: pendingRequest.request, requestId: pendingRequest.requestId, mcpConnected: true, pluginConnected: true }); } else { res.json({ request: null, mcpConnected: true, pluginConnected: true }); } }); // Response endpoint for Studio plugin app.post('/response', (req, res) => { const { requestId, response, error } = req.body; if (error) { bridge.rejectRequest(requestId, error); } else { bridge.resolveRequest(requestId, response); } res.json({ success: true }); }); // Middleware to track MCP activity for all MCP endpoints app.use('/mcp/*', (req, res, next) => { trackMCPActivity(); next(); }); // MCP tool proxy endpoints - these will be called by AI tools app.post('/mcp/get_file_tree', async (req, res) => { try { const result = await tools.getFileTree(req.body.path); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/search_files', async (req, res) => { try { const result = await tools.searchFiles(req.body.query, req.body.searchType); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/get_place_info', async (req, res) => { try { const result = await tools.getPlaceInfo(); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/get_services', async (req, res) => { try { const result = await tools.getServices(req.body.serviceName); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/search_objects', async (req, res) => { try { const result = await tools.searchObjects(req.body.query, req.body.searchType, req.body.propertyName); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/get_instance_properties', async (req, res) => { try { const result = await tools.getInstanceProperties(req.body.instancePath); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/get_instance_children', async (req, res) => { try { const result = await tools.getInstanceChildren(req.body.instancePath); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/search_by_property', async (req, res) => { try { const result = await tools.searchByProperty(req.body.propertyName, req.body.propertyValue); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/get_class_info', async (req, res) => { try { const result = await tools.getClassInfo(req.body.className); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/mass_set_property', async (req, res) => { try { const result = await tools.massSetProperty(req.body.paths, req.body.propertyName, req.body.propertyValue); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/mass_get_property', async (req, res) => { try { const result = await tools.massGetProperty(req.body.paths, req.body.propertyName); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/create_object_with_properties', async (req, res) => { try { const result = await tools.createObjectWithProperties(req.body.className, req.body.parent, req.body.name, req.body.properties); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/mass_create_objects', async (req, res) => { try { const result = await tools.massCreateObjects(req.body.objects); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/mass_create_objects_with_properties', async (req, res) => { try { const result = await tools.massCreateObjectsWithProperties(req.body.objects); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/get_project_structure', async (req, res) => { try { const result = await tools.getProjectStructure(req.body.path, req.body.maxDepth, req.body.scriptsOnly); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); // Script management endpoints (parity with tools) app.post('/mcp/get_script_source', async (req, res) => { try { const result = await tools.getScriptSource(req.body.instancePath); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); app.post('/mcp/set_script_source', async (req, res) => { try { const result = await tools.setScriptSource(req.body.instancePath, req.body.source); res.json(result); } catch (error) { res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }); } }); // Add methods to control and check server status (app as any).isPluginConnected = isPluginConnected; (app as any).setMCPServerActive = setMCPServerActive; (app as any).isMCPServerActive = isMCPServerActive; (app as any).trackMCPActivity = trackMCPActivity; return app; }

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