@el4cteo/rbx-studio-mcp 0.7.7 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -175
- package/dist/bridge/harness.js +14 -5
- package/dist/bridge/harness.js.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/apidump.js +38 -23
- package/dist/lib/apidump.js.map +1 -1
- package/dist/lib/format.js +43 -4
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/pluginbuild.js +10 -0
- package/dist/lib/pluginbuild.js.map +1 -1
- package/dist/lib/png.js +104 -0
- package/dist/lib/png.js.map +1 -1
- package/dist/resources.js +3 -2
- package/dist/resources.js.map +1 -1
- package/dist/tools/api.js +4 -3
- package/dist/tools/api.js.map +1 -1
- package/dist/tools/debug.js +2 -2
- package/dist/tools/debug.js.map +1 -1
- package/dist/tools/device.js +2 -2
- package/dist/tools/device.js.map +1 -1
- package/dist/tools/discover.js +7 -3
- package/dist/tools/discover.js.map +1 -1
- package/dist/tools/exec.js +4 -4
- package/dist/tools/exec.js.map +1 -1
- package/dist/tools/instances.js +32 -7
- package/dist/tools/instances.js.map +1 -1
- package/dist/tools/perf.js +35 -13
- package/dist/tools/perf.js.map +1 -1
- package/dist/tools/playtest.js +34 -13
- package/dist/tools/playtest.js.map +1 -1
- package/dist/tools/screenshot.js +55 -11
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/scripts.js +4 -4
- package/dist/tools/scripts.js.map +1 -1
- package/dist/tools/terrain.js +3 -3
- package/dist/tools/terrain.js.map +1 -1
- package/dist/tools/world.js +8 -8
- package/dist/tools/world.js.map +1 -1
- package/package.json +75 -75
- package/plugin/src/ClientRelay.luau +128 -1
- package/plugin/src/Config.luau +1 -1
- package/plugin/src/LogBuffer.luau +363 -277
- package/plugin/src/Paths.luau +15 -4
- package/plugin/src/Phrase.luau +1 -20
- package/plugin/src/ScriptEdit.luau +14 -0
- package/plugin/src/Serialize.luau +17 -0
- package/plugin/src/TextEdit.luau +6 -0
- package/plugin/src/handlers/Api.luau +8 -0
- package/plugin/src/handlers/Capture.luau +219 -3
- package/plugin/src/handlers/Perf.luau +913 -889
- package/plugin/src/handlers/Playtest.luau +6 -1
- package/plugin/src/init.server.luau +3 -2
- package/scripts/test-live.mjs +106 -1
- package/scripts/test-plugin.mjs +2 -0
- package/scripts/test-tools.mjs +259 -162
- package/dist/tools/anim.js +0 -159
- package/dist/tools/anim.js.map +0 -1
- package/plugin/src/handlers/Anim.luau +0 -897
package/package.json
CHANGED
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@el4cteo/rbx-studio-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for Roblox Studio.
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"bin": {
|
|
8
|
-
"rbx-studio-mcp": "dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"plugin",
|
|
13
|
-
"scripts",
|
|
14
|
-
"config/dsh.cordis.yml",
|
|
15
|
-
"README.md",
|
|
16
|
-
"LICENSE"
|
|
17
|
-
],
|
|
18
|
-
"dsh": {
|
|
19
|
-
"bundle": {
|
|
20
|
-
"patch": "./config/dsh.cordis.yml"
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"engines": {
|
|
24
|
-
"node": ">=22.15"
|
|
25
|
-
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "tsc",
|
|
28
|
-
"watch": "tsc --watch",
|
|
29
|
-
"typecheck": "tsc --noEmit",
|
|
30
|
-
"start": "node dist/index.js",
|
|
31
|
-
"build:plugin": "node scripts/check-plugin.mjs && node scripts/build-plugin.mjs",
|
|
32
|
-
"sourcemap": "node scripts/sourcemap.mjs",
|
|
33
|
-
"test:live": "node scripts/test-live.mjs",
|
|
34
|
-
"test": "node scripts/test-plugin.mjs && node scripts/test-transport.mjs && node scripts/test-server.mjs && node scripts/test-bridge.mjs && node scripts/test-console.mjs && node scripts/test-failover.mjs && node scripts/test-tools.mjs",
|
|
35
|
-
"build:all": "npm run build && npm run build:plugin",
|
|
36
|
-
"install:plugin": "node scripts/install-plugin.mjs",
|
|
37
|
-
"check:plugin": "node scripts/check-plugin.mjs",
|
|
38
|
-
"doctor": "node dist/index.js doctor",
|
|
39
|
-
"prepack": "npm run build"
|
|
40
|
-
},
|
|
41
|
-
"keywords": [
|
|
42
|
-
"mcp",
|
|
43
|
-
"mcp-server",
|
|
44
|
-
"model-context-protocol",
|
|
45
|
-
"roblox",
|
|
46
|
-
"roblox-studio",
|
|
47
|
-
"roblox-development",
|
|
48
|
-
"luau",
|
|
49
|
-
"claude",
|
|
50
|
-
"claude-code",
|
|
51
|
-
"cursor",
|
|
52
|
-
"ai",
|
|
53
|
-
"ai-agent"
|
|
54
|
-
],
|
|
55
|
-
"dependencies": {
|
|
56
|
-
"@modelcontextprotocol/sdk": "^1.30.
|
|
57
|
-
"zod": "^4.6.5"
|
|
58
|
-
},
|
|
59
|
-
"devDependencies": {
|
|
60
|
-
"@types/node": "^26.6.2",
|
|
61
|
-
"typescript": "^7.0.2"
|
|
62
|
-
},
|
|
63
|
-
"author": "EL4CTEO",
|
|
64
|
-
"repository": {
|
|
65
|
-
"type": "git",
|
|
66
|
-
"url": "git+https://github.com/EL4CTEO/rbx-studio-mcp.git"
|
|
67
|
-
},
|
|
68
|
-
"homepage": "https://github.com/EL4CTEO/rbx-studio-mcp#readme",
|
|
69
|
-
"bugs": {
|
|
70
|
-
"url": "https://github.com/EL4CTEO/rbx-studio-mcp/issues"
|
|
71
|
-
},
|
|
72
|
-
"publishConfig": {
|
|
73
|
-
"access": "public"
|
|
74
|
-
}
|
|
75
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@el4cteo/rbx-studio-mcp",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "MCP server for Roblox Studio. 34 tools, push-based SSE bridge, editor-safe script edits, one-step undo.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"rbx-studio-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"plugin",
|
|
13
|
+
"scripts",
|
|
14
|
+
"config/dsh.cordis.yml",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"dsh": {
|
|
19
|
+
"bundle": {
|
|
20
|
+
"patch": "./config/dsh.cordis.yml"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=22.15"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"watch": "tsc --watch",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"start": "node dist/index.js",
|
|
31
|
+
"build:plugin": "node scripts/check-plugin.mjs && node scripts/build-plugin.mjs",
|
|
32
|
+
"sourcemap": "node scripts/sourcemap.mjs",
|
|
33
|
+
"test:live": "node scripts/test-live.mjs",
|
|
34
|
+
"test": "node scripts/test-plugin.mjs && node scripts/test-transport.mjs && node scripts/test-server.mjs && node scripts/test-bridge.mjs && node scripts/test-console.mjs && node scripts/test-failover.mjs && node scripts/test-tools.mjs",
|
|
35
|
+
"build:all": "npm run build && npm run build:plugin",
|
|
36
|
+
"install:plugin": "node scripts/install-plugin.mjs",
|
|
37
|
+
"check:plugin": "node scripts/check-plugin.mjs",
|
|
38
|
+
"doctor": "node dist/index.js doctor",
|
|
39
|
+
"prepack": "npm run build"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"mcp",
|
|
43
|
+
"mcp-server",
|
|
44
|
+
"model-context-protocol",
|
|
45
|
+
"roblox",
|
|
46
|
+
"roblox-studio",
|
|
47
|
+
"roblox-development",
|
|
48
|
+
"luau",
|
|
49
|
+
"claude",
|
|
50
|
+
"claude-code",
|
|
51
|
+
"cursor",
|
|
52
|
+
"ai",
|
|
53
|
+
"ai-agent"
|
|
54
|
+
],
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
57
|
+
"zod": "^4.6.5"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/node": "^26.6.2",
|
|
61
|
+
"typescript": "^7.0.2"
|
|
62
|
+
},
|
|
63
|
+
"author": "EL4CTEO",
|
|
64
|
+
"repository": {
|
|
65
|
+
"type": "git",
|
|
66
|
+
"url": "git+https://github.com/EL4CTEO/rbx-studio-mcp.git"
|
|
67
|
+
},
|
|
68
|
+
"homepage": "https://github.com/EL4CTEO/rbx-studio-mcp#readme",
|
|
69
|
+
"bugs": {
|
|
70
|
+
"url": "https://github.com/EL4CTEO/rbx-studio-mcp/issues"
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -1,9 +1,136 @@
|
|
|
1
1
|
--!strict
|
|
2
|
-
--
|
|
2
|
+
-- Shared client relay lifecycles for temporary calls and persistent diagnostics.
|
|
3
3
|
local Players = game:GetService("Players")
|
|
4
4
|
local RunService = game:GetService("RunService")
|
|
5
5
|
local Dispatch = require(script.Parent.Dispatch)
|
|
6
|
+
local LogBuffer = require(script.Parent.LogBuffer)
|
|
6
7
|
local ClientRelay = {}
|
|
8
|
+
|
|
9
|
+
-- Long-lived diagnostics use the same LocalScript/RemoteEvent replication path
|
|
10
|
+
-- as temporary client calls. The server owns the buffers and validates the
|
|
11
|
+
-- sender; the client never needs HTTP access.
|
|
12
|
+
local DIAGNOSTIC_SOURCE = [==[
|
|
13
|
+
local LogService = game:GetService("LogService")
|
|
14
|
+
local ScriptContext = game:GetService("ScriptContext")
|
|
15
|
+
local report = script:WaitForChild("Report", 10)
|
|
16
|
+
if not report then return end
|
|
17
|
+
local open = true
|
|
18
|
+
local connections = {}
|
|
19
|
+
local levels = {
|
|
20
|
+
[Enum.MessageType.MessageOutput] = "print",
|
|
21
|
+
[Enum.MessageType.MessageInfo] = "info",
|
|
22
|
+
[Enum.MessageType.MessageWarning] = "warning",
|
|
23
|
+
[Enum.MessageType.MessageError] = "error",
|
|
24
|
+
}
|
|
25
|
+
local function send(payload)
|
|
26
|
+
if open and report.Parent then report:FireServer(payload) end
|
|
27
|
+
end
|
|
28
|
+
table.insert(connections, LogService.MessageOut:Connect(function(message, kind)
|
|
29
|
+
send({level = levels[kind] or "print", message = string.sub(message, 1, 2000), timestamp = os.time()})
|
|
30
|
+
end))
|
|
31
|
+
pcall(function()
|
|
32
|
+
table.insert(connections, ScriptContext.Error:Connect(function(message, stack, source)
|
|
33
|
+
local path
|
|
34
|
+
if typeof(source) == "Instance" then
|
|
35
|
+
local ok, full = pcall(function() return source:GetFullName() end)
|
|
36
|
+
path = if ok then full else source.Name
|
|
37
|
+
end
|
|
38
|
+
send({kind = "trace", message = string.sub(message, 1, 2000), stack = string.sub(stack, 1, 4000), source = path})
|
|
39
|
+
end))
|
|
40
|
+
end)
|
|
41
|
+
script.Destroying:Connect(function()
|
|
42
|
+
open = false
|
|
43
|
+
for _, connection in connections do connection:Disconnect() end
|
|
44
|
+
end)
|
|
45
|
+
send({kind = "ready"})
|
|
46
|
+
]==]
|
|
47
|
+
|
|
48
|
+
local diagnosticsStarted = false
|
|
49
|
+
local diagnosticConnections: { [Player]: RBXScriptConnection } = {}
|
|
50
|
+
local diagnosticRelays: { [Player]: LocalScript } = {}
|
|
51
|
+
local diagnosticInstalling: { [Player]: boolean } = {}
|
|
52
|
+
local characterConnections: { [Player]: RBXScriptConnection } = {}
|
|
53
|
+
local playerAddedConnection: RBXScriptConnection? = nil
|
|
54
|
+
local playerRemovingConnection: RBXScriptConnection? = nil
|
|
55
|
+
local function installDiagnostics(player: Player)
|
|
56
|
+
if diagnosticInstalling[player] or diagnosticConnections[player] then return end
|
|
57
|
+
diagnosticInstalling[player] = true
|
|
58
|
+
task.spawn(function()
|
|
59
|
+
-- PlayerGui is server-visible (PlayerScripts is created on the client).
|
|
60
|
+
local gui = player:FindFirstChildOfClass("PlayerGui") or player:WaitForChild("PlayerGui", 15)
|
|
61
|
+
if not diagnosticsStarted or not gui or player.Parent ~= Players then diagnosticInstalling[player] = nil; return end
|
|
62
|
+
local old = gui:FindFirstChild("MCPDiagnosticsRelay")
|
|
63
|
+
if old then old:Destroy() end
|
|
64
|
+
LogBuffer.startClient(player)
|
|
65
|
+
local relay = Instance.new("LocalScript")
|
|
66
|
+
local remote = Instance.new("RemoteEvent")
|
|
67
|
+
local ok = pcall(function()
|
|
68
|
+
relay.Name = "MCPDiagnosticsRelay"
|
|
69
|
+
relay:SetAttribute("MCPClientRelay", true)
|
|
70
|
+
relay.Source = DIAGNOSTIC_SOURCE
|
|
71
|
+
remote.Name = "Report"
|
|
72
|
+
remote.Parent = relay
|
|
73
|
+
diagnosticConnections[player] = remote.OnServerEvent:Connect(function(from, payload)
|
|
74
|
+
if from == player then LogBuffer.pushClient(player, payload) end
|
|
75
|
+
end)
|
|
76
|
+
relay.Parent = gui
|
|
77
|
+
diagnosticRelays[player] = relay
|
|
78
|
+
end)
|
|
79
|
+
if not ok then
|
|
80
|
+
if diagnosticConnections[player] then diagnosticConnections[player]:Disconnect() end
|
|
81
|
+
diagnosticConnections[player] = nil
|
|
82
|
+
relay:Destroy()
|
|
83
|
+
LogBuffer.stopClient(player)
|
|
84
|
+
end
|
|
85
|
+
diagnosticInstalling[player] = nil
|
|
86
|
+
if not characterConnections[player] then
|
|
87
|
+
characterConnections[player] = player.CharacterAdded:Connect(function()
|
|
88
|
+
task.delay(0.5, function()
|
|
89
|
+
if not diagnosticsStarted or player.Parent ~= Players then return end
|
|
90
|
+
local currentGui = player:FindFirstChildOfClass("PlayerGui")
|
|
91
|
+
local current = diagnosticRelays[player]
|
|
92
|
+
if current and current.Parent == currentGui then return end
|
|
93
|
+
if diagnosticConnections[player] then diagnosticConnections[player]:Disconnect() end
|
|
94
|
+
diagnosticConnections[player], diagnosticRelays[player] = nil, nil
|
|
95
|
+
installDiagnostics(player)
|
|
96
|
+
end)
|
|
97
|
+
end)
|
|
98
|
+
end
|
|
99
|
+
end)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
function ClientRelay.startDiagnostics()
|
|
103
|
+
if diagnosticsStarted or not RunService:IsRunning() or not RunService:IsServer() then return end
|
|
104
|
+
diagnosticsStarted = true
|
|
105
|
+
playerAddedConnection = Players.PlayerAdded:Connect(installDiagnostics)
|
|
106
|
+
playerRemovingConnection = Players.PlayerRemoving:Connect(function(player)
|
|
107
|
+
if diagnosticConnections[player] then diagnosticConnections[player]:Disconnect() end
|
|
108
|
+
diagnosticConnections[player] = nil
|
|
109
|
+
if diagnosticRelays[player] then diagnosticRelays[player]:Destroy() end
|
|
110
|
+
diagnosticRelays[player] = nil
|
|
111
|
+
if characterConnections[player] then characterConnections[player]:Disconnect() end
|
|
112
|
+
characterConnections[player] = nil
|
|
113
|
+
LogBuffer.stopClient(player)
|
|
114
|
+
end)
|
|
115
|
+
for _, player in Players:GetPlayers() do installDiagnostics(player) end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
function ClientRelay.stopDiagnostics()
|
|
119
|
+
if not diagnosticsStarted then return end
|
|
120
|
+
diagnosticsStarted = false
|
|
121
|
+
if playerAddedConnection then playerAddedConnection:Disconnect() end
|
|
122
|
+
if playerRemovingConnection then playerRemovingConnection:Disconnect() end
|
|
123
|
+
playerAddedConnection, playerRemovingConnection = nil, nil
|
|
124
|
+
for player, connection in diagnosticConnections do
|
|
125
|
+
connection:Disconnect()
|
|
126
|
+
if diagnosticRelays[player] then diagnosticRelays[player]:Destroy() end
|
|
127
|
+
LogBuffer.stopClient(player)
|
|
128
|
+
end
|
|
129
|
+
for _, connection in characterConnections do connection:Disconnect() end
|
|
130
|
+
table.clear(diagnosticConnections)
|
|
131
|
+
table.clear(diagnosticRelays)
|
|
132
|
+
table.clear(characterConnections)
|
|
133
|
+
end
|
|
7
134
|
function ClientRelay.playerFor(name: string?): Player
|
|
8
135
|
if not RunService:IsRunning() then
|
|
9
136
|
Dispatch.fail("NOT_RUNNING", "Client access needs a running playtest; this is an edit session.")
|
package/plugin/src/Config.luau
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
local Config = {}
|
|
11
11
|
|
|
12
|
-
Config.PLUGIN_VERSION = "0.
|
|
12
|
+
Config.PLUGIN_VERSION = "0.8.0"
|
|
13
13
|
|
|
14
14
|
-- Fingerprint of plugin/src, stamped in by scripts/build-plugin.mjs. The server
|
|
15
15
|
-- computes the same hash from its own copy of the sources and compares, so a
|