local HttpService = game:GetService("HttpService")
local PORT = 3000
local WS_URL = "ws://localhost:" .. PORT
local function debugLog(...)
print("[MCP-Bridge]", ...)
end
-- Helper to find instance by path string
-- e.g. "game.Workspace.Part"
local function findInstance(path)
if path == "game" then return game end
local segments = {}
for match in (path .. "."):gmatch("(.-)%.") do
table.insert(segments, match)
end
if segments[1] == "game" then
table.remove(segments, 1)
end
local current = game
for _, name in ipairs(segments) do
if not current then return nil end
current = current:FindFirstChild(name)
end
return current
end
local function getPath(instance)
return instance:GetFullName()
end
local function safeDecompile(scriptInstance)
-- Check for exploit-specific decompile functions
if getgenv and getgenv().decompile then
return getgenv().decompile(scriptInstance)
elseif decompile then
return decompile(scriptInstance)
else
-- Fallback for Studio or when decompile is missing
local success, source = pcall(function() return scriptInstance.Source end)
if success then return source end
return "-- Decompile not supported in this environment and .Source is hidden/protected"
end
end
local function isServerScript(instance)
-- Check for server-side containers
-- Note: on client, these services might not even exist or contain the instance in the tree if not replicated
local serverContainers = {"ServerScriptService", "ServerStorage"}
for _, containerName in ipairs(serverContainers) do
-- Use pcall in case service is restricted
local success, container = pcall(function() return game:GetService(containerName) end)
if success and container and instance:IsDescendantOf(container) then
return true, "Instance is inside a server-only container (" .. containerName .. ")"
end
end
-- Check for Script class (Server/Legacy RunContext)
if instance:IsA("Script") then
-- RunContext was added recently, handle case where it might not exist (older Roblox versions?)
-- though MCP is likely running on up-to-date client.
local success, runContext = pcall(function() return instance.RunContext end)
if success then
-- Enum.RunContext.Legacy or Enum.RunContext.Server
if runContext == Enum.RunContext.Legacy or runContext == Enum.RunContext.Server then
return true, "Server script source is not replicated to the client"
end
else
-- If RunContext property doesn't exist, it's a legacy server script
return true, "Server script source is not replicated to the client"
end
end
return false
end
local commands = {}
function commands.list_children(params)
local path = params.path or "game"
local instance = findInstance(path)
if not instance then error("Instance not found: " .. path) end
local children = instance:GetChildren()
local result = {}
for _, child in ipairs(children) do
table.insert(result, {
name = child.Name,
className = child.ClassName,
path = getPath(child)
})
end
return result
end
function commands.get_properties(params)
local path = params.path
local instance = findInstance(path)
if not instance then error("Instance not found: " .. path) end
local propsToCheck = params.properties or {"Name", "ClassName", "Position", "Size", "Transparency", "Anchored", "CanCollide", "Text", "Value"}
local result = {}
for _, prop in ipairs(propsToCheck) do
local success, val = pcall(function() return instance[prop] end)
if success then
result[prop] = tostring(val) -- Convert to string for safety
end
end
return result
end
function commands.read_script(params)
local path = params.path
local instance = findInstance(path)
if not instance then error("Instance not found: " .. path) end
if not instance:IsA("LuaSourceContainer") then error("Instance is not a script") end
local isServer, reason = isServerScript(instance)
if isServer then
error("Cannot read server-side script from client executor: " .. reason)
end
return safeDecompile(instance)
end
function commands.run_script(params)
local code = params.code
if not code then error("No code provided") end
-- In an exploit environment, loadstring is usually available
local loadFn = loadstring
if not loadFn then
error("loadstring is not available in this environment")
end
local func, err = loadFn(code)
if not func then error("Compilation error: " .. tostring(err)) end
local results = {pcall(func)}
local success = table.remove(results, 1)
if not success then
error(tostring(results[1]))
end
-- Convert return values to string representation
local stringResults = {}
for _, v in ipairs(results) do
table.insert(stringResults, tostring(v))
end
return stringResults
end
local function handleCommand(cmd)
local method = cmd.method
local params = cmd.params or {}
debugLog("Executing", method)
local handler = commands[method]
if not handler then
error("Unknown method: " .. tostring(method))
end
return handler(params)
end
local function connect()
debugLog("Connecting to " .. WS_URL)
local success, socket = pcall(function()
return WebSocket.connect(WS_URL)
end)
if not success then
debugLog("Failed to connect:", socket)
return
end
debugLog("Connected!")
socket.OnMessage:Connect(function(msg)
local cmd = HttpService:JSONDecode(msg)
-- Run command safely
local successExec, resultOrErr = pcall(function()
return handleCommand(cmd)
end)
local reply = {
id = cmd.id,
success = successExec
}
if successExec then
reply.result = resultOrErr
else
reply.error = tostring(resultOrErr)
end
socket:Send(HttpService:JSONEncode(reply))
end)
socket.OnClose:Connect(function()
debugLog("Disconnected")
end)
end
connect()