@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.
Files changed (59) hide show
  1. package/README.md +175 -175
  2. package/dist/bridge/harness.js +14 -5
  3. package/dist/bridge/harness.js.map +1 -1
  4. package/dist/index.js +5 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/apidump.js +38 -23
  7. package/dist/lib/apidump.js.map +1 -1
  8. package/dist/lib/format.js +43 -4
  9. package/dist/lib/format.js.map +1 -1
  10. package/dist/lib/pluginbuild.js +10 -0
  11. package/dist/lib/pluginbuild.js.map +1 -1
  12. package/dist/lib/png.js +104 -0
  13. package/dist/lib/png.js.map +1 -1
  14. package/dist/resources.js +3 -2
  15. package/dist/resources.js.map +1 -1
  16. package/dist/tools/api.js +4 -3
  17. package/dist/tools/api.js.map +1 -1
  18. package/dist/tools/debug.js +2 -2
  19. package/dist/tools/debug.js.map +1 -1
  20. package/dist/tools/device.js +2 -2
  21. package/dist/tools/device.js.map +1 -1
  22. package/dist/tools/discover.js +7 -3
  23. package/dist/tools/discover.js.map +1 -1
  24. package/dist/tools/exec.js +4 -4
  25. package/dist/tools/exec.js.map +1 -1
  26. package/dist/tools/instances.js +32 -7
  27. package/dist/tools/instances.js.map +1 -1
  28. package/dist/tools/perf.js +35 -13
  29. package/dist/tools/perf.js.map +1 -1
  30. package/dist/tools/playtest.js +34 -13
  31. package/dist/tools/playtest.js.map +1 -1
  32. package/dist/tools/screenshot.js +55 -11
  33. package/dist/tools/screenshot.js.map +1 -1
  34. package/dist/tools/scripts.js +4 -4
  35. package/dist/tools/scripts.js.map +1 -1
  36. package/dist/tools/terrain.js +3 -3
  37. package/dist/tools/terrain.js.map +1 -1
  38. package/dist/tools/world.js +8 -8
  39. package/dist/tools/world.js.map +1 -1
  40. package/package.json +75 -75
  41. package/plugin/src/ClientRelay.luau +128 -1
  42. package/plugin/src/Config.luau +1 -1
  43. package/plugin/src/LogBuffer.luau +363 -277
  44. package/plugin/src/Paths.luau +15 -4
  45. package/plugin/src/Phrase.luau +1 -20
  46. package/plugin/src/ScriptEdit.luau +14 -0
  47. package/plugin/src/Serialize.luau +17 -0
  48. package/plugin/src/TextEdit.luau +6 -0
  49. package/plugin/src/handlers/Api.luau +8 -0
  50. package/plugin/src/handlers/Capture.luau +219 -3
  51. package/plugin/src/handlers/Perf.luau +913 -889
  52. package/plugin/src/handlers/Playtest.luau +6 -1
  53. package/plugin/src/init.server.luau +3 -2
  54. package/scripts/test-live.mjs +106 -1
  55. package/scripts/test-plugin.mjs +2 -0
  56. package/scripts/test-tools.mjs +259 -162
  57. package/dist/tools/anim.js +0 -159
  58. package/dist/tools/anim.js.map +0 -1
  59. 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.7.7",
4
- "description": "MCP server for Roblox Studio. 35 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.0",
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
- -- One temporary client relay lifecycle for input and execution.
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.")
@@ -9,7 +9,7 @@
9
9
 
10
10
  local Config = {}
11
11
 
12
- Config.PLUGIN_VERSION = "0.7.7"
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