Add syncback command handling to MCP stream and API

- Introduced a new `onSyncbackCommand` option in `McpStream` to handle syncback requests.
- Implemented the `handle_mcp_syncback` function in the API to process syncback requests, including validation and logging.
- Updated the `run_live_syncback` function to return statistics on added and removed files during the syncback process.
- Enhanced the MCP tools list to include the new syncback tool, with corresponding documentation added.

These changes aim to facilitate a complete syncback of the Roblox Studio state to the filesystem, improving project management capabilities.
This commit is contained in:
Joe 2026-03-01 18:49:34 -06:00
parent fc8a6e9b9c
commit c5502191d3
7 changed files with 248 additions and 24 deletions

View file

@ -215,6 +215,43 @@ function App:init()
onGetScriptCommand = function(requestId, data)
return self:handleMcpGetScript(requestId, data)
end,
onSyncbackCommand = function(requestId)
return Promise.new(function(resolve)
local host = Config.defaultHost
local port = Config.defaultPort
self:collectAndPostSyncback(host, port, "/api/mcp/syncback")
:andThen(function(httpResponse)
local message = "Syncback complete."
local decodeOk, decoded = pcall(function()
return httpResponse:msgpack()
end)
if decodeOk and type(decoded) == "table" then
local added = decoded.added or 0
local removed = decoded.removed or 0
message = string.format(
"Syncback complete: wrote %d files/folders, removed %d.",
added,
removed
)
end
Log.info("MCP syncback complete")
resolve({
requestId = requestId,
status = "success",
response = message,
})
end)
:catch(function(err)
Log.warn("MCP syncback failed: {}", tostring(err))
resolve({
requestId = requestId,
status = "error",
response = "Syncback failed: " .. tostring(err),
})
end)
end)
end,
onToolCommand = function(requestId, toolType, args)
return Promise.new(function(resolve)
local ok, result = pcall(McpTools.dispatch, toolType, args)
@ -680,19 +717,7 @@ function App:useRunningConnectionInfo()
self.setPort(port)
end
function App:performSyncback()
self:setState({ showingSyncbackConfirm = false })
local host = self.host:getValue()
local port = self.port:getValue()
if host == "" then
host = Config.defaultHost
end
if port == "" then
port = Config.defaultPort
end
function App:collectAndPostSyncback(host, port, endpoint)
local services = {}
local allChildren = {}
local allCarriers = {}
@ -720,7 +745,7 @@ function App:performSyncback()
carrier:Destroy()
end
local url = ("http://%s:%s/api/syncback"):format(host, port)
local url = ("http://%s:%s%s"):format(host, port, endpoint)
local body = Http.msgpackEncode({
protocolVersion = Config.protocolVersion,
serverVersion = Config.expectedServerVersionString,
@ -729,7 +754,23 @@ function App:performSyncback()
services = services,
})
Http.post(url, body)
return Http.post(url, body)
end
function App:performSyncback()
self:setState({ showingSyncbackConfirm = false })
local host = self.host:getValue()
local port = self.port:getValue()
if host == "" then
host = Config.defaultHost
end
if port == "" then
port = Config.defaultPort
end
self:collectAndPostSyncback(host, port, "/api/syncback")
:andThen(function()
Log.info("Syncback data sent to server.")
self:addNotification({

View file

@ -28,6 +28,7 @@ function McpStream.new(options)
self._onSyncCommand = options.onSyncCommand
self._onGetScriptCommand = options.onGetScriptCommand
self._onSyncbackCommand = options.onSyncbackCommand
self._onToolCommand = options.onToolCommand
self._getPluginConfig = options.getPluginConfig
self._wsClient = nil
@ -178,6 +179,33 @@ function McpStream:_tryConnect()
end)
end
end)
elseif data.type == "syncback" and data.requestId then
Log.info("MCP stream: received syncback command (requestId={})", data.requestId)
local resultPromise = self._onSyncbackCommand(data.requestId)
resultPromise
:andThen(function(result)
if self._wsClient then
local json = HttpService:JSONEncode(result)
pcall(function()
self._wsClient:Send(json)
end)
end
end)
:catch(function(err)
Log.warn("MCP stream: syncback command failed: {}", tostring(err))
if self._wsClient then
local errorResult = HttpService:JSONEncode({
requestId = data.requestId,
status = "error",
response = tostring(err),
})
pcall(function()
self._wsClient:Send(errorResult)
end)
end
end)
elseif data.type and data.requestId and PASSTHROUGH_TOOLS[data.type] then
Log.info("MCP stream: received {} command (requestId={})", data.type, data.requestId)