rbxstudio-mcp 2.3.1 → 2.4.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 +67 -14
- package/dist/__tests__/bridge-service.test.js +25 -13
- package/dist/__tests__/bridge-service.test.js.map +1 -1
- package/dist/__tests__/bridge-session.test.d.ts +2 -0
- package/dist/__tests__/bridge-session.test.d.ts.map +1 -0
- package/dist/__tests__/bridge-session.test.js +171 -0
- package/dist/__tests__/bridge-session.test.js.map +1 -0
- package/dist/__tests__/chunker.test.d.ts +2 -0
- package/dist/__tests__/chunker.test.d.ts.map +1 -0
- package/dist/__tests__/chunker.test.js +201 -0
- package/dist/__tests__/chunker.test.js.map +1 -0
- package/dist/__tests__/docs-core.test.d.ts +2 -0
- package/dist/__tests__/docs-core.test.d.ts.map +1 -0
- package/dist/__tests__/docs-core.test.js +137 -0
- package/dist/__tests__/docs-core.test.js.map +1 -0
- package/dist/__tests__/docs-fetcher.test.d.ts +2 -0
- package/dist/__tests__/docs-fetcher.test.d.ts.map +1 -0
- package/dist/__tests__/docs-fetcher.test.js +173 -0
- package/dist/__tests__/docs-fetcher.test.js.map +1 -0
- package/dist/__tests__/helpers.d.ts +8 -0
- package/dist/__tests__/helpers.d.ts.map +1 -0
- package/dist/__tests__/helpers.js +23 -0
- package/dist/__tests__/helpers.js.map +1 -0
- package/dist/__tests__/http-routes.test.d.ts +2 -0
- package/dist/__tests__/http-routes.test.d.ts.map +1 -0
- package/dist/__tests__/http-routes.test.js +233 -0
- package/dist/__tests__/http-routes.test.js.map +1 -0
- package/dist/__tests__/http-server.test.js +13 -6
- package/dist/__tests__/http-server.test.js.map +1 -1
- package/dist/__tests__/integration.test.js +9 -4
- package/dist/__tests__/integration.test.js.map +1 -1
- package/dist/__tests__/semantic-search.test.d.ts +2 -0
- package/dist/__tests__/semantic-search.test.d.ts.map +1 -0
- package/dist/__tests__/semantic-search.test.js +202 -0
- package/dist/__tests__/semantic-search.test.js.map +1 -0
- package/dist/__tests__/smoke.test.js +7 -3
- package/dist/__tests__/smoke.test.js.map +1 -1
- package/dist/__tests__/studio-client.test.d.ts +2 -0
- package/dist/__tests__/studio-client.test.d.ts.map +1 -0
- package/dist/__tests__/studio-client.test.js +25 -0
- package/dist/__tests__/studio-client.test.js.map +1 -0
- package/dist/__tests__/tool-nudges.test.d.ts +2 -0
- package/dist/__tests__/tool-nudges.test.d.ts.map +1 -0
- package/dist/__tests__/tool-nudges.test.js +60 -0
- package/dist/__tests__/tool-nudges.test.js.map +1 -0
- package/dist/__tests__/tool-registry.test.d.ts +2 -0
- package/dist/__tests__/tool-registry.test.d.ts.map +1 -0
- package/dist/__tests__/tool-registry.test.js +365 -0
- package/dist/__tests__/tool-registry.test.js.map +1 -0
- package/dist/__tests__/tools-bridge.test.d.ts +2 -0
- package/dist/__tests__/tools-bridge.test.d.ts.map +1 -0
- package/dist/__tests__/tools-bridge.test.js +396 -0
- package/dist/__tests__/tools-bridge.test.js.map +1 -0
- package/dist/__tests__/tools-docs.test.d.ts +2 -0
- package/dist/__tests__/tools-docs.test.d.ts.map +1 -0
- package/dist/__tests__/tools-docs.test.js +112 -0
- package/dist/__tests__/tools-docs.test.js.map +1 -0
- package/dist/__tests__/tools-guards.test.d.ts +2 -0
- package/dist/__tests__/tools-guards.test.d.ts.map +1 -0
- package/dist/__tests__/tools-guards.test.js +131 -0
- package/dist/__tests__/tools-guards.test.js.map +1 -0
- package/dist/__tests__/tools-runtime.test.d.ts +2 -0
- package/dist/__tests__/tools-runtime.test.d.ts.map +1 -0
- package/dist/__tests__/tools-runtime.test.js +214 -0
- package/dist/__tests__/tools-runtime.test.js.map +1 -0
- package/dist/__tests__/tools-visual.test.d.ts +2 -0
- package/dist/__tests__/tools-visual.test.d.ts.map +1 -0
- package/dist/__tests__/tools-visual.test.js +149 -0
- package/dist/__tests__/tools-visual.test.js.map +1 -0
- package/dist/bridge-service.d.ts +99 -12
- package/dist/bridge-service.d.ts.map +1 -1
- package/dist/bridge-service.js +238 -21
- package/dist/bridge-service.js.map +1 -1
- package/dist/docs/cache.d.ts +50 -0
- package/dist/docs/cache.d.ts.map +1 -0
- package/dist/docs/cache.js +123 -0
- package/dist/docs/cache.js.map +1 -0
- package/dist/docs/embeddings/chunker.d.ts +120 -0
- package/dist/docs/embeddings/chunker.d.ts.map +1 -0
- package/dist/docs/embeddings/chunker.js +395 -0
- package/dist/docs/embeddings/chunker.js.map +1 -0
- package/dist/docs/embeddings/embedder.d.ts +41 -0
- package/dist/docs/embeddings/embedder.d.ts.map +1 -0
- package/dist/docs/embeddings/embedder.js +113 -0
- package/dist/docs/embeddings/embedder.js.map +1 -0
- package/dist/docs/embeddings/index.d.ts +102 -0
- package/dist/docs/embeddings/index.d.ts.map +1 -0
- package/dist/docs/embeddings/index.js +250 -0
- package/dist/docs/embeddings/index.js.map +1 -0
- package/dist/docs/embeddings/manager.d.ts +68 -0
- package/dist/docs/embeddings/manager.d.ts.map +1 -0
- package/dist/docs/embeddings/manager.js +97 -0
- package/dist/docs/embeddings/manager.js.map +1 -0
- package/dist/docs/fetcher.d.ts +29 -0
- package/dist/docs/fetcher.d.ts.map +1 -0
- package/dist/docs/fetcher.js +244 -0
- package/dist/docs/fetcher.js.map +1 -0
- package/dist/docs/reference.d.ts +37 -0
- package/dist/docs/reference.d.ts.map +1 -0
- package/dist/docs/reference.js +108 -0
- package/dist/docs/reference.js.map +1 -0
- package/dist/docs/search.d.ts +194 -0
- package/dist/docs/search.d.ts.map +1 -0
- package/dist/docs/search.js +733 -0
- package/dist/docs/search.js.map +1 -0
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +52 -5
- package/dist/http-server.js.map +1 -1
- package/dist/index.d.ts +8 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -1035
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +15 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +26 -0
- package/dist/instructions.js.map +1 -0
- package/dist/tools/defs/attributes.d.ts +6 -0
- package/dist/tools/defs/attributes.d.ts.map +1 -0
- package/dist/tools/defs/attributes.js +85 -0
- package/dist/tools/defs/attributes.js.map +1 -0
- package/dist/tools/defs/docs.d.ts +17 -0
- package/dist/tools/defs/docs.d.ts.map +1 -0
- package/dist/tools/defs/docs.js +151 -0
- package/dist/tools/defs/docs.js.map +1 -0
- package/dist/tools/defs/execute.d.ts +6 -0
- package/dist/tools/defs/execute.d.ts.map +1 -0
- package/dist/tools/defs/execute.js +21 -0
- package/dist/tools/defs/execute.js.map +1 -0
- package/dist/tools/defs/inspection.d.ts +7 -0
- package/dist/tools/defs/inspection.d.ts.map +1 -0
- package/dist/tools/defs/inspection.js +202 -0
- package/dist/tools/defs/inspection.js.map +1 -0
- package/dist/tools/defs/objects.d.ts +6 -0
- package/dist/tools/defs/objects.d.ts.map +1 -0
- package/dist/tools/defs/objects.js +111 -0
- package/dist/tools/defs/objects.js.map +1 -0
- package/dist/tools/defs/properties.d.ts +6 -0
- package/dist/tools/defs/properties.d.ts.map +1 -0
- package/dist/tools/defs/properties.js +71 -0
- package/dist/tools/defs/properties.js.map +1 -0
- package/dist/tools/defs/runtime.d.ts +6 -0
- package/dist/tools/defs/runtime.d.ts.map +1 -0
- package/dist/tools/defs/runtime.js +145 -0
- package/dist/tools/defs/runtime.js.map +1 -0
- package/dist/tools/defs/scripts.d.ts +18 -0
- package/dist/tools/defs/scripts.d.ts.map +1 -0
- package/dist/tools/defs/scripts.js +163 -0
- package/dist/tools/defs/scripts.js.map +1 -0
- package/dist/tools/defs/tags.d.ts +6 -0
- package/dist/tools/defs/tags.d.ts.map +1 -0
- package/dist/tools/defs/tags.js +74 -0
- package/dist/tools/defs/tags.js.map +1 -0
- package/dist/tools/defs/visual.d.ts +7 -0
- package/dist/tools/defs/visual.d.ts.map +1 -0
- package/dist/tools/defs/visual.js +208 -0
- package/dist/tools/defs/visual.js.map +1 -0
- package/dist/tools/index.d.ts +101 -25
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +580 -63
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/nudges.d.ts +25 -0
- package/dist/tools/nudges.d.ts.map +1 -0
- package/dist/tools/nudges.js +34 -0
- package/dist/tools/nudges.js.map +1 -0
- package/dist/tools/registry.d.ts +20 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +65 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +24 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/package.json +7 -6
- package/studio-plugin/MCPPlugin.rbxmx +3 -238
- package/studio-plugin/plugin.luau +2041 -365
package/dist/index.js
CHANGED
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Roblox Studio MCP Server
|
|
4
4
|
*
|
|
5
|
-
* This server provides Model Context Protocol (MCP) tools for interacting
|
|
6
|
-
* It allows AI assistants to access Studio data,
|
|
5
|
+
* This server provides Model Context Protocol (MCP) tools for interacting
|
|
6
|
+
* with Roblox Studio. It allows AI assistants to access Studio data,
|
|
7
|
+
* scripts, and objects through a bridge plugin.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* Tools are defined declaratively in `src/tools/defs/*.ts` and aggregated
|
|
10
|
+
* by `src/tools/registry.ts`. To add a new tool, edit one of those files —
|
|
11
|
+
* this bootstrap doesn't need to change.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* "command": "npx",
|
|
14
|
-
* "args": ["-y", "robloxstudio-mcp"]
|
|
15
|
-
* }
|
|
13
|
+
* Usage:
|
|
14
|
+
* npx rbxstudio-mcp
|
|
16
15
|
*/
|
|
17
16
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
18
17
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
@@ -20,6 +19,8 @@ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } f
|
|
|
20
19
|
import { createHttpServer } from './http-server.js';
|
|
21
20
|
import { RobloxStudioTools } from './tools/index.js';
|
|
22
21
|
import { BridgeService } from './bridge-service.js';
|
|
22
|
+
import { allTools, toolsByName, applyNudge } from './tools/registry.js';
|
|
23
|
+
import { SERVER_INSTRUCTIONS } from './instructions.js';
|
|
23
24
|
class RobloxStudioMCPServer {
|
|
24
25
|
server;
|
|
25
26
|
tools;
|
|
@@ -32,1039 +33,35 @@ class RobloxStudioMCPServer {
|
|
|
32
33
|
capabilities: {
|
|
33
34
|
tools: {},
|
|
34
35
|
},
|
|
36
|
+
// Always-on operating manual (mental model + cross-tool workflow);
|
|
37
|
+
// returned in the initialize response and usually folded into the
|
|
38
|
+
// client's system prompt. See src/instructions.ts.
|
|
39
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
35
40
|
});
|
|
36
41
|
this.bridge = new BridgeService();
|
|
37
42
|
this.tools = new RobloxStudioTools(this.bridge);
|
|
38
43
|
this.setupToolHandlers();
|
|
39
44
|
}
|
|
40
45
|
setupToolHandlers() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
properties: {}
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: 'get_services',
|
|
55
|
-
description: 'Get available Roblox services and their children',
|
|
56
|
-
inputSchema: {
|
|
57
|
-
type: 'object',
|
|
58
|
-
properties: {
|
|
59
|
-
serviceName: {
|
|
60
|
-
type: 'string',
|
|
61
|
-
description: 'Optional specific service name to query'
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
// Property & Instance Tools
|
|
67
|
-
{
|
|
68
|
-
name: 'get_instance_properties',
|
|
69
|
-
description: 'Get all properties of a specific Roblox instance in Studio',
|
|
70
|
-
inputSchema: {
|
|
71
|
-
type: 'object',
|
|
72
|
-
properties: {
|
|
73
|
-
instancePath: {
|
|
74
|
-
type: 'string',
|
|
75
|
-
description: 'Roblox instance path using dot notation (e.g., "game.Workspace.Part", "game.ServerScriptService.MainScript", "game.ReplicatedStorage.ModuleScript")'
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
required: ['instancePath']
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: 'get_class_info',
|
|
83
|
-
description: 'Get available properties/methods for Roblox classes',
|
|
84
|
-
inputSchema: {
|
|
85
|
-
type: 'object',
|
|
86
|
-
properties: {
|
|
87
|
-
className: {
|
|
88
|
-
type: 'string',
|
|
89
|
-
description: 'Roblox class name'
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
required: ['className']
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
// Project Tools
|
|
96
|
-
{
|
|
97
|
-
name: 'get_project_structure',
|
|
98
|
-
description: 'Get complete game hierarchy. IMPORTANT: Use maxDepth parameter (default: 3) to explore deeper levels of the hierarchy. Set higher values like 5-10 for comprehensive exploration',
|
|
99
|
-
inputSchema: {
|
|
100
|
-
type: 'object',
|
|
101
|
-
properties: {
|
|
102
|
-
path: {
|
|
103
|
-
type: 'string',
|
|
104
|
-
description: 'Optional path to start from (defaults to workspace root)',
|
|
105
|
-
default: ''
|
|
106
|
-
},
|
|
107
|
-
maxDepth: {
|
|
108
|
-
type: 'number',
|
|
109
|
-
description: 'Maximum depth to traverse (default: 3). RECOMMENDED: Use 5-10 for thorough exploration. Higher values provide more complete structure',
|
|
110
|
-
default: 3
|
|
111
|
-
},
|
|
112
|
-
scriptsOnly: {
|
|
113
|
-
type: 'boolean',
|
|
114
|
-
description: 'Show only scripts and script containers',
|
|
115
|
-
default: false
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
// Property Modification Tools
|
|
121
|
-
{
|
|
122
|
-
name: 'set_property',
|
|
123
|
-
description: 'Set a property on any Roblox instance',
|
|
124
|
-
inputSchema: {
|
|
125
|
-
type: 'object',
|
|
126
|
-
properties: {
|
|
127
|
-
instancePath: {
|
|
128
|
-
type: 'string',
|
|
129
|
-
description: 'Path to the instance (e.g., "game.Workspace.Part")'
|
|
130
|
-
},
|
|
131
|
-
propertyName: {
|
|
132
|
-
type: 'string',
|
|
133
|
-
description: 'Name of the property to set'
|
|
134
|
-
},
|
|
135
|
-
propertyValue: {
|
|
136
|
-
description: 'Value to set the property to (any type)'
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
required: ['instancePath', 'propertyName', 'propertyValue']
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
name: 'mass_set_property',
|
|
144
|
-
description: 'Set the same property on multiple instances at once',
|
|
145
|
-
inputSchema: {
|
|
146
|
-
type: 'object',
|
|
147
|
-
properties: {
|
|
148
|
-
paths: {
|
|
149
|
-
type: 'array',
|
|
150
|
-
items: { type: 'string' },
|
|
151
|
-
description: 'Array of instance paths to modify'
|
|
152
|
-
},
|
|
153
|
-
propertyName: {
|
|
154
|
-
type: 'string',
|
|
155
|
-
description: 'Name of the property to set'
|
|
156
|
-
},
|
|
157
|
-
propertyValue: {
|
|
158
|
-
description: 'Value to set the property to (any type)'
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
required: ['paths', 'propertyName', 'propertyValue']
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
name: 'mass_get_property',
|
|
166
|
-
description: 'Get the same property from multiple instances at once',
|
|
167
|
-
inputSchema: {
|
|
168
|
-
type: 'object',
|
|
169
|
-
properties: {
|
|
170
|
-
paths: {
|
|
171
|
-
type: 'array',
|
|
172
|
-
items: { type: 'string' },
|
|
173
|
-
description: 'Array of instance paths to read from'
|
|
174
|
-
},
|
|
175
|
-
propertyName: {
|
|
176
|
-
type: 'string',
|
|
177
|
-
description: 'Name of the property to get'
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
required: ['paths', 'propertyName']
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
// Object Creation/Deletion Tools
|
|
184
|
-
{
|
|
185
|
-
name: 'create_object',
|
|
186
|
-
description: 'Create a new Roblox object instance (basic, without properties)',
|
|
187
|
-
inputSchema: {
|
|
188
|
-
type: 'object',
|
|
189
|
-
properties: {
|
|
190
|
-
className: {
|
|
191
|
-
type: 'string',
|
|
192
|
-
description: 'Roblox class name (e.g., "Part", "Script", "Folder")'
|
|
193
|
-
},
|
|
194
|
-
parent: {
|
|
195
|
-
type: 'string',
|
|
196
|
-
description: 'Path to the parent instance (e.g., "game.Workspace")'
|
|
197
|
-
},
|
|
198
|
-
name: {
|
|
199
|
-
type: 'string',
|
|
200
|
-
description: 'Optional name for the new object'
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
required: ['className', 'parent']
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
name: 'delete_object',
|
|
208
|
-
description: 'Delete a Roblox object instance',
|
|
209
|
-
inputSchema: {
|
|
210
|
-
type: 'object',
|
|
211
|
-
properties: {
|
|
212
|
-
instancePath: {
|
|
213
|
-
type: 'string',
|
|
214
|
-
description: 'Path to the instance to delete'
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
required: ['instancePath']
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
// Script Management Tools (for Roblox Studio scripts - NOT local files)
|
|
221
|
-
{
|
|
222
|
-
name: 'get_script_source',
|
|
223
|
-
description: 'Get the source code of a Roblox script (LocalScript, Script, or ModuleScript). Returns both "source" (raw code) and "numberedSource" (with line numbers prefixed like "1: code"). Use numberedSource to accurately identify line numbers for editing. For large scripts (>1500 lines), use startLine/endLine to read specific sections.',
|
|
224
|
-
inputSchema: {
|
|
225
|
-
type: 'object',
|
|
226
|
-
properties: {
|
|
227
|
-
instancePath: {
|
|
228
|
-
type: 'string',
|
|
229
|
-
description: 'Roblox instance path to the script using dot notation (e.g., "game.ServerScriptService.MainScript", "game.StarterPlayer.StarterPlayerScripts.LocalScript")'
|
|
230
|
-
},
|
|
231
|
-
startLine: {
|
|
232
|
-
type: 'number',
|
|
233
|
-
description: 'Optional: Start line number (1-indexed). Use for reading specific sections of large scripts.'
|
|
234
|
-
},
|
|
235
|
-
endLine: {
|
|
236
|
-
type: 'number',
|
|
237
|
-
description: 'Optional: End line number (inclusive). Use for reading specific sections of large scripts.'
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
required: ['instancePath']
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
name: 'set_script_source',
|
|
245
|
-
description: 'Replace the entire source code of a Roblox script. Uses ScriptEditorService:UpdateSourceAsync (works with open editors). For partial edits, prefer edit_script_lines, insert_script_lines, or delete_script_lines.',
|
|
246
|
-
inputSchema: {
|
|
247
|
-
type: 'object',
|
|
248
|
-
properties: {
|
|
249
|
-
instancePath: {
|
|
250
|
-
type: 'string',
|
|
251
|
-
description: 'Roblox instance path to the script (e.g., "game.ServerScriptService.MainScript")'
|
|
252
|
-
},
|
|
253
|
-
source: {
|
|
254
|
-
type: 'string',
|
|
255
|
-
description: 'New source code for the script'
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
required: ['instancePath', 'source']
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
// Partial Script Editing Tools - use "numberedSource" from get_script_source to identify correct line numbers
|
|
262
|
-
{
|
|
263
|
-
name: 'edit_script_lines',
|
|
264
|
-
description: 'Replace specific lines in a Roblox script without rewriting the entire source. IMPORTANT: Use the "numberedSource" field from get_script_source to identify the correct line numbers. Lines are 1-indexed and ranges are inclusive.',
|
|
265
|
-
inputSchema: {
|
|
266
|
-
type: 'object',
|
|
267
|
-
properties: {
|
|
268
|
-
instancePath: {
|
|
269
|
-
type: 'string',
|
|
270
|
-
description: 'Roblox instance path to the script (e.g., "game.ServerScriptService.MainScript")'
|
|
271
|
-
},
|
|
272
|
-
startLine: {
|
|
273
|
-
type: 'number',
|
|
274
|
-
description: 'First line to replace (1-indexed). Get this from the "numberedSource" field.'
|
|
275
|
-
},
|
|
276
|
-
endLine: {
|
|
277
|
-
type: 'number',
|
|
278
|
-
description: 'Last line to replace (inclusive). Get this from the "numberedSource" field.'
|
|
279
|
-
},
|
|
280
|
-
newContent: {
|
|
281
|
-
type: 'string',
|
|
282
|
-
description: 'New content to replace the specified lines (can be multiple lines separated by newlines)'
|
|
283
|
-
}
|
|
284
|
-
},
|
|
285
|
-
required: ['instancePath', 'startLine', 'endLine', 'newContent']
|
|
286
|
-
}
|
|
287
|
-
},
|
|
288
|
-
{
|
|
289
|
-
name: 'insert_script_lines',
|
|
290
|
-
description: 'Insert new lines into a Roblox script at a specific position. IMPORTANT: Use the "numberedSource" field from get_script_source to identify the correct line numbers.',
|
|
291
|
-
inputSchema: {
|
|
292
|
-
type: 'object',
|
|
293
|
-
properties: {
|
|
294
|
-
instancePath: {
|
|
295
|
-
type: 'string',
|
|
296
|
-
description: 'Roblox instance path to the script (e.g., "game.ServerScriptService.MainScript")'
|
|
297
|
-
},
|
|
298
|
-
afterLine: {
|
|
299
|
-
type: 'number',
|
|
300
|
-
description: 'Insert after this line number (0 = insert at very beginning, 1 = after first line). Get line numbers from "numberedSource".',
|
|
301
|
-
default: 0
|
|
302
|
-
},
|
|
303
|
-
newContent: {
|
|
304
|
-
type: 'string',
|
|
305
|
-
description: 'Content to insert (can be multiple lines separated by newlines)'
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
required: ['instancePath', 'newContent']
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
name: 'delete_script_lines',
|
|
313
|
-
description: 'Delete specific lines from a Roblox script. IMPORTANT: Use the "numberedSource" field from get_script_source to identify the correct line numbers.',
|
|
314
|
-
inputSchema: {
|
|
315
|
-
type: 'object',
|
|
316
|
-
properties: {
|
|
317
|
-
instancePath: {
|
|
318
|
-
type: 'string',
|
|
319
|
-
description: 'Roblox instance path to the script (e.g., "game.ServerScriptService.MainScript")'
|
|
320
|
-
},
|
|
321
|
-
startLine: {
|
|
322
|
-
type: 'number',
|
|
323
|
-
description: 'First line to delete (1-indexed). Get this from the "numberedSource" field.'
|
|
324
|
-
},
|
|
325
|
-
endLine: {
|
|
326
|
-
type: 'number',
|
|
327
|
-
description: 'Last line to delete (inclusive). Get this from the "numberedSource" field.'
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
required: ['instancePath', 'startLine', 'endLine']
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
// ============================================
|
|
334
|
-
// CLAUDE CODE-STYLE SCRIPT EDITING TOOLS
|
|
335
|
-
// ============================================
|
|
336
|
-
{
|
|
337
|
-
name: 'edit_script',
|
|
338
|
-
description: 'RECOMMENDED: String-based script editing like Claude Code\'s Edit tool. Find exact text and replace it - no line numbers needed! This is the safest and most reliable way to edit scripts. The edit will FAIL safely if old_string is not found or appears multiple times (unless replace_all is true). Always validates syntax after editing.',
|
|
339
|
-
inputSchema: {
|
|
340
|
-
type: 'object',
|
|
341
|
-
properties: {
|
|
342
|
-
instancePath: {
|
|
343
|
-
type: 'string',
|
|
344
|
-
description: 'Roblox instance path to the script (e.g., "game.ServerScriptService.MainScript")'
|
|
345
|
-
},
|
|
346
|
-
old_string: {
|
|
347
|
-
type: 'string',
|
|
348
|
-
description: 'The exact text to find and replace (must match exactly, including whitespace/indentation)'
|
|
349
|
-
},
|
|
350
|
-
new_string: {
|
|
351
|
-
type: 'string',
|
|
352
|
-
description: 'The text to replace it with (must be different from old_string)'
|
|
353
|
-
},
|
|
354
|
-
replace_all: {
|
|
355
|
-
type: 'boolean',
|
|
356
|
-
description: 'If true, replace ALL occurrences of old_string. If false (default), fails if old_string appears more than once.',
|
|
357
|
-
default: false
|
|
358
|
-
},
|
|
359
|
-
validate_after: {
|
|
360
|
-
type: 'boolean',
|
|
361
|
-
description: 'If true (default), validates the script syntax after editing and reverts if invalid.',
|
|
362
|
-
default: true
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
required: ['instancePath', 'old_string', 'new_string']
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
name: 'search_script',
|
|
370
|
-
description: 'Search for patterns within a script source code (like grep). Returns matching lines with line numbers and optional context.',
|
|
371
|
-
inputSchema: {
|
|
372
|
-
type: 'object',
|
|
373
|
-
properties: {
|
|
374
|
-
instancePath: {
|
|
375
|
-
type: 'string',
|
|
376
|
-
description: 'Roblox instance path to the script (e.g., "game.ServerScriptService.MainScript")'
|
|
377
|
-
},
|
|
378
|
-
pattern: {
|
|
379
|
-
type: 'string',
|
|
380
|
-
description: 'Search pattern (literal string or regex if use_regex is true)'
|
|
381
|
-
},
|
|
382
|
-
use_regex: {
|
|
383
|
-
type: 'boolean',
|
|
384
|
-
description: 'If true, treat pattern as a Lua regex pattern. Default false (literal match).',
|
|
385
|
-
default: false
|
|
386
|
-
},
|
|
387
|
-
context_lines: {
|
|
388
|
-
type: 'number',
|
|
389
|
-
description: 'Number of lines to show before and after each match (like grep -C). Default 0.',
|
|
390
|
-
default: 0
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
required: ['instancePath', 'pattern']
|
|
394
|
-
}
|
|
395
|
-
},
|
|
396
|
-
{
|
|
397
|
-
name: 'get_script_function',
|
|
398
|
-
description: 'Extract a specific function from a script by name. Returns the function source code with start/end line numbers. Perfect for editing just one function without affecting the rest of the script.',
|
|
399
|
-
inputSchema: {
|
|
400
|
-
type: 'object',
|
|
401
|
-
properties: {
|
|
402
|
-
instancePath: {
|
|
403
|
-
type: 'string',
|
|
404
|
-
description: 'Roblox instance path to the script (e.g., "game.ServerScriptService.MainScript")'
|
|
405
|
-
},
|
|
406
|
-
function_name: {
|
|
407
|
-
type: 'string',
|
|
408
|
-
description: 'Name of the function to extract (e.g., "onPlayerJoin", "handleDamage")'
|
|
409
|
-
}
|
|
410
|
-
},
|
|
411
|
-
required: ['instancePath', 'function_name']
|
|
412
|
-
}
|
|
413
|
-
},
|
|
414
|
-
{
|
|
415
|
-
name: 'find_and_replace_in_scripts',
|
|
416
|
-
description: 'Find and replace text across multiple scripts at once. Like edit_script but for batch operations. Validates all scripts after editing.',
|
|
417
|
-
inputSchema: {
|
|
418
|
-
type: 'object',
|
|
419
|
-
properties: {
|
|
420
|
-
paths: {
|
|
421
|
-
type: 'array',
|
|
422
|
-
items: { type: 'string' },
|
|
423
|
-
description: 'Array of script paths to search and replace in'
|
|
424
|
-
},
|
|
425
|
-
old_string: {
|
|
426
|
-
type: 'string',
|
|
427
|
-
description: 'The exact text to find and replace'
|
|
428
|
-
},
|
|
429
|
-
new_string: {
|
|
430
|
-
type: 'string',
|
|
431
|
-
description: 'The text to replace it with'
|
|
432
|
-
},
|
|
433
|
-
validate_after: {
|
|
434
|
-
type: 'boolean',
|
|
435
|
-
description: 'If true (default), validates syntax after each edit.',
|
|
436
|
-
default: true
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
|
-
required: ['paths', 'old_string', 'new_string']
|
|
440
|
-
}
|
|
441
|
-
},
|
|
442
|
-
// Attribute Tools (for Roblox instance attributes)
|
|
443
|
-
{
|
|
444
|
-
name: 'get_attribute',
|
|
445
|
-
description: 'Get a single attribute value from a Roblox instance',
|
|
446
|
-
inputSchema: {
|
|
447
|
-
type: 'object',
|
|
448
|
-
properties: {
|
|
449
|
-
instancePath: {
|
|
450
|
-
type: 'string',
|
|
451
|
-
description: 'Roblox instance path using dot notation (e.g., "game.Workspace.Part", "game.ServerStorage.DataStore")'
|
|
452
|
-
},
|
|
453
|
-
attributeName: {
|
|
454
|
-
type: 'string',
|
|
455
|
-
description: 'Name of the attribute to get'
|
|
456
|
-
}
|
|
457
|
-
},
|
|
458
|
-
required: ['instancePath', 'attributeName']
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
{
|
|
462
|
-
name: 'set_attribute',
|
|
463
|
-
description: 'Set an attribute value on a Roblox instance. Supports string, number, boolean, Vector3, Color3, UDim2, and BrickColor.',
|
|
464
|
-
inputSchema: {
|
|
465
|
-
type: 'object',
|
|
466
|
-
properties: {
|
|
467
|
-
instancePath: {
|
|
468
|
-
type: 'string',
|
|
469
|
-
description: 'Roblox instance path using dot notation (e.g., "game.Workspace.Part")'
|
|
470
|
-
},
|
|
471
|
-
attributeName: {
|
|
472
|
-
type: 'string',
|
|
473
|
-
description: 'Name of the attribute to set'
|
|
474
|
-
},
|
|
475
|
-
attributeValue: {
|
|
476
|
-
description: 'Value to set. For Vector3: {X, Y, Z}, Color3: {R, G, B}, UDim2: {X: {Scale, Offset}, Y: {Scale, Offset}}'
|
|
477
|
-
},
|
|
478
|
-
valueType: {
|
|
479
|
-
type: 'string',
|
|
480
|
-
description: 'Optional type hint: "Vector3", "Color3", "UDim2", "BrickColor"'
|
|
481
|
-
}
|
|
482
|
-
},
|
|
483
|
-
required: ['instancePath', 'attributeName', 'attributeValue']
|
|
484
|
-
}
|
|
485
|
-
},
|
|
486
|
-
{
|
|
487
|
-
name: 'get_attributes',
|
|
488
|
-
description: 'Get all attributes on a Roblox instance',
|
|
489
|
-
inputSchema: {
|
|
490
|
-
type: 'object',
|
|
491
|
-
properties: {
|
|
492
|
-
instancePath: {
|
|
493
|
-
type: 'string',
|
|
494
|
-
description: 'Roblox instance path using dot notation (e.g., "game.Workspace.Part")'
|
|
495
|
-
}
|
|
496
|
-
},
|
|
497
|
-
required: ['instancePath']
|
|
498
|
-
}
|
|
499
|
-
},
|
|
500
|
-
{
|
|
501
|
-
name: 'delete_attribute',
|
|
502
|
-
description: 'Delete an attribute from a Roblox instance',
|
|
503
|
-
inputSchema: {
|
|
504
|
-
type: 'object',
|
|
505
|
-
properties: {
|
|
506
|
-
instancePath: {
|
|
507
|
-
type: 'string',
|
|
508
|
-
description: 'Roblox instance path using dot notation (e.g., "game.Workspace.Part")'
|
|
509
|
-
},
|
|
510
|
-
attributeName: {
|
|
511
|
-
type: 'string',
|
|
512
|
-
description: 'Name of the attribute to delete'
|
|
513
|
-
}
|
|
514
|
-
},
|
|
515
|
-
required: ['instancePath', 'attributeName']
|
|
516
|
-
}
|
|
517
|
-
},
|
|
518
|
-
// Tag Tools (CollectionService) - for Roblox instance tags
|
|
519
|
-
{
|
|
520
|
-
name: 'get_tags',
|
|
521
|
-
description: 'Get all CollectionService tags on a Roblox instance',
|
|
522
|
-
inputSchema: {
|
|
523
|
-
type: 'object',
|
|
524
|
-
properties: {
|
|
525
|
-
instancePath: {
|
|
526
|
-
type: 'string',
|
|
527
|
-
description: 'Roblox instance path using dot notation (e.g., "game.Workspace.Part")'
|
|
528
|
-
}
|
|
529
|
-
},
|
|
530
|
-
required: ['instancePath']
|
|
531
|
-
}
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
name: 'add_tag',
|
|
535
|
-
description: 'Add a CollectionService tag to a Roblox instance',
|
|
536
|
-
inputSchema: {
|
|
537
|
-
type: 'object',
|
|
538
|
-
properties: {
|
|
539
|
-
instancePath: {
|
|
540
|
-
type: 'string',
|
|
541
|
-
description: 'Roblox instance path using dot notation (e.g., "game.Workspace.Part")'
|
|
542
|
-
},
|
|
543
|
-
tagName: {
|
|
544
|
-
type: 'string',
|
|
545
|
-
description: 'Name of the tag to add'
|
|
546
|
-
}
|
|
547
|
-
},
|
|
548
|
-
required: ['instancePath', 'tagName']
|
|
549
|
-
}
|
|
550
|
-
},
|
|
551
|
-
{
|
|
552
|
-
name: 'remove_tag',
|
|
553
|
-
description: 'Remove a CollectionService tag from a Roblox instance',
|
|
554
|
-
inputSchema: {
|
|
555
|
-
type: 'object',
|
|
556
|
-
properties: {
|
|
557
|
-
instancePath: {
|
|
558
|
-
type: 'string',
|
|
559
|
-
description: 'Roblox instance path using dot notation (e.g., "game.Workspace.Part")'
|
|
560
|
-
},
|
|
561
|
-
tagName: {
|
|
562
|
-
type: 'string',
|
|
563
|
-
description: 'Name of the tag to remove'
|
|
564
|
-
}
|
|
565
|
-
},
|
|
566
|
-
required: ['instancePath', 'tagName']
|
|
567
|
-
}
|
|
568
|
-
},
|
|
569
|
-
{
|
|
570
|
-
name: 'get_tagged',
|
|
571
|
-
description: 'Get all instances with a specific tag',
|
|
572
|
-
inputSchema: {
|
|
573
|
-
type: 'object',
|
|
574
|
-
properties: {
|
|
575
|
-
tagName: {
|
|
576
|
-
type: 'string',
|
|
577
|
-
description: 'Name of the tag to search for'
|
|
578
|
-
}
|
|
579
|
-
},
|
|
580
|
-
required: ['tagName']
|
|
581
|
-
}
|
|
582
|
-
},
|
|
583
|
-
{
|
|
584
|
-
name: 'get_selection',
|
|
585
|
-
description: 'Get all currently selected objects',
|
|
586
|
-
inputSchema: {
|
|
587
|
-
type: 'object',
|
|
588
|
-
properties: {}
|
|
589
|
-
}
|
|
590
|
-
},
|
|
591
|
-
// ============================================
|
|
592
|
-
// OUTPUT CAPTURE TOOL
|
|
593
|
-
// ============================================
|
|
594
|
-
{
|
|
595
|
-
name: 'get_output',
|
|
596
|
-
description: 'Read the Output window content from Roblox Studio. Captures print(), warn(), and error() messages. Use after play_solo to debug scripts.',
|
|
597
|
-
inputSchema: {
|
|
598
|
-
type: 'object',
|
|
599
|
-
properties: {
|
|
600
|
-
limit: {
|
|
601
|
-
type: 'number',
|
|
602
|
-
description: 'Maximum number of messages to return (default: 100)',
|
|
603
|
-
default: 100
|
|
604
|
-
},
|
|
605
|
-
since: {
|
|
606
|
-
type: 'number',
|
|
607
|
-
description: 'Only return messages after this Unix timestamp'
|
|
608
|
-
},
|
|
609
|
-
messageTypes: {
|
|
610
|
-
type: 'array',
|
|
611
|
-
items: { type: 'string' },
|
|
612
|
-
description: 'Filter by message type: MessageOutput, MessageInfo, MessageWarning, MessageError'
|
|
613
|
-
},
|
|
614
|
-
clear: {
|
|
615
|
-
type: 'boolean',
|
|
616
|
-
description: 'Clear the output buffer after reading (default: false)',
|
|
617
|
-
default: false
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
},
|
|
622
|
-
// ============================================
|
|
623
|
-
// INSTANCE MANIPULATION TOOLS
|
|
624
|
-
// ============================================
|
|
625
|
-
{
|
|
626
|
-
name: 'clone_instance',
|
|
627
|
-
description: 'Clone (copy) a Roblox instance to a new parent location. Creates a deep copy including all children and properties.',
|
|
628
|
-
inputSchema: {
|
|
629
|
-
type: 'object',
|
|
630
|
-
properties: {
|
|
631
|
-
sourcePath: {
|
|
632
|
-
type: 'string',
|
|
633
|
-
description: 'Path to the instance to clone (e.g., "game.Workspace.walkietalkie")'
|
|
634
|
-
},
|
|
635
|
-
targetParent: {
|
|
636
|
-
type: 'string',
|
|
637
|
-
description: 'Path to the new parent (e.g., "game.ReplicatedStorage")'
|
|
638
|
-
},
|
|
639
|
-
newName: {
|
|
640
|
-
type: 'string',
|
|
641
|
-
description: 'Optional new name for the cloned instance'
|
|
642
|
-
}
|
|
643
|
-
},
|
|
644
|
-
required: ['sourcePath', 'targetParent']
|
|
645
|
-
}
|
|
646
|
-
},
|
|
647
|
-
{
|
|
648
|
-
name: 'move_instance',
|
|
649
|
-
description: 'Move a Roblox instance to a new parent location. Changes the Parent property.',
|
|
650
|
-
inputSchema: {
|
|
651
|
-
type: 'object',
|
|
652
|
-
properties: {
|
|
653
|
-
instancePath: {
|
|
654
|
-
type: 'string',
|
|
655
|
-
description: 'Path to the instance to move (e.g., "game.Workspace.Tool")'
|
|
656
|
-
},
|
|
657
|
-
newParent: {
|
|
658
|
-
type: 'string',
|
|
659
|
-
description: 'Path to the new parent (e.g., "game.StarterPack")'
|
|
660
|
-
}
|
|
661
|
-
},
|
|
662
|
-
required: ['instancePath', 'newParent']
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
|
-
// ============================================
|
|
666
|
-
// SCRIPT VALIDATION TOOL
|
|
667
|
-
// ============================================
|
|
668
|
-
{
|
|
669
|
-
name: 'validate_script',
|
|
670
|
-
description: 'Validate Lua/Luau script syntax without running it. Returns syntax errors and warnings for deprecated patterns (wait, spawn, delay). Can validate either an existing script or raw source code.',
|
|
671
|
-
inputSchema: {
|
|
672
|
-
type: 'object',
|
|
673
|
-
properties: {
|
|
674
|
-
instancePath: {
|
|
675
|
-
type: 'string',
|
|
676
|
-
description: 'Path to the script to validate (e.g., "game.ServerScriptService.MainScript")'
|
|
677
|
-
},
|
|
678
|
-
source: {
|
|
679
|
-
type: 'string',
|
|
680
|
-
description: 'Raw Lua source code to validate (alternative to instancePath)'
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
},
|
|
685
|
-
// ============================================
|
|
686
|
-
// UNDO/REDO TOOLS
|
|
687
|
-
// ============================================
|
|
688
|
-
{
|
|
689
|
-
name: 'undo',
|
|
690
|
-
description: 'Undo the last change made in Roblox Studio. All MCP mutations are automatically recorded for undo support. Use this to revert mistakes.',
|
|
691
|
-
inputSchema: {
|
|
692
|
-
type: 'object',
|
|
693
|
-
properties: {}
|
|
694
|
-
}
|
|
695
|
-
},
|
|
696
|
-
{
|
|
697
|
-
name: 'redo',
|
|
698
|
-
description: 'Redo a previously undone change in Roblox Studio.',
|
|
699
|
-
inputSchema: {
|
|
700
|
-
type: 'object',
|
|
701
|
-
properties: {}
|
|
702
|
-
}
|
|
703
|
-
},
|
|
704
|
-
// ============================================
|
|
705
|
-
// ASSET INSERTION TOOL (Creator Store)
|
|
706
|
-
// ============================================
|
|
707
|
-
{
|
|
708
|
-
name: 'insert_asset',
|
|
709
|
-
description: 'Download and insert a Creator Store asset (model, package, etc.) into Roblox Studio for reference. Uses game:GetObjects() which works with any free/public asset. Perfect for loading reference code, example implementations, or asset libraries that the AI can then read and analyze using get_instance_children and get_script_source.',
|
|
710
|
-
inputSchema: {
|
|
711
|
-
type: 'object',
|
|
712
|
-
properties: {
|
|
713
|
-
assetId: {
|
|
714
|
-
type: 'number',
|
|
715
|
-
description: 'The Creator Store asset ID (the number from the asset URL, e.g., 104116977416770)'
|
|
716
|
-
},
|
|
717
|
-
folderName: {
|
|
718
|
-
type: 'string',
|
|
719
|
-
description: 'Name of the folder to create/use for storing assets (default: "AIReferences")',
|
|
720
|
-
default: 'AIReferences'
|
|
721
|
-
},
|
|
722
|
-
targetParent: {
|
|
723
|
-
type: 'string',
|
|
724
|
-
description: 'Parent path where the folder should be created (default: "game.Workspace"). Use "game.ReplicatedStorage" or "game.ServerStorage" to keep assets out of the visible workspace.',
|
|
725
|
-
default: 'game.Workspace'
|
|
726
|
-
}
|
|
727
|
-
},
|
|
728
|
-
required: ['assetId']
|
|
729
|
-
}
|
|
730
|
-
},
|
|
731
|
-
// ============================================
|
|
732
|
-
// PLAYTEST CONTROL TOOLS
|
|
733
|
-
// ============================================
|
|
734
|
-
{
|
|
735
|
-
name: 'play_solo',
|
|
736
|
-
description: 'Start a play test (Play Solo) in Roblox Studio via StudioTestService:ExecutePlayModeAsync. Automatically injects an in-test companion script so stop_play and get_playtest_output work. If a previous test is still tracked, stops it first. Returns a sessionId that ties together start, stop, and output reads.',
|
|
737
|
-
inputSchema: {
|
|
738
|
-
type: 'object',
|
|
739
|
-
properties: {}
|
|
740
|
-
}
|
|
741
|
-
},
|
|
742
|
-
{
|
|
743
|
-
name: 'stop_play',
|
|
744
|
-
description: 'Stop the current play test cleanly via StudioTestService:EndTest (called from inside the test by an injected companion script). Restores pre-play state — unlike RunService:Stop, this is the proper way to end a Play Solo session. Idempotent: returns successfully if no test is running.',
|
|
745
|
-
inputSchema: {
|
|
746
|
-
type: 'object',
|
|
747
|
-
properties: {}
|
|
748
|
-
}
|
|
749
|
-
},
|
|
750
|
-
{
|
|
751
|
-
name: 'get_playtest_output',
|
|
752
|
-
description: 'Read script output (print/warn/error) captured DURING a play test session. Streamed live from the test\'s Server DataModel by the injected companion. Survives after the test ends so you can debug post-hoc. For non-playtest output (Edit-mode plugin output, build messages), use get_output instead. Use the sinceSeq cursor returned in nextSinceSeq to tail incrementally.',
|
|
753
|
-
inputSchema: {
|
|
754
|
-
type: 'object',
|
|
755
|
-
properties: {
|
|
756
|
-
sinceSeq: {
|
|
757
|
-
type: 'number',
|
|
758
|
-
description: 'Only return entries with seq > this value. Pass back nextSinceSeq from a prior call to avoid re-reading.',
|
|
759
|
-
},
|
|
760
|
-
limit: {
|
|
761
|
-
type: 'number',
|
|
762
|
-
description: 'Max entries to return (default 500, max 5000).',
|
|
763
|
-
default: 500,
|
|
764
|
-
},
|
|
765
|
-
messageTypes: {
|
|
766
|
-
type: 'array',
|
|
767
|
-
items: { type: 'string' },
|
|
768
|
-
description: 'Filter by MessageType: MessageOutput, MessageInfo, MessageWarning, MessageError.',
|
|
769
|
-
},
|
|
770
|
-
},
|
|
771
|
-
},
|
|
772
|
-
},
|
|
773
|
-
// ============================================
|
|
774
|
-
// SCREENSHOT TOOL
|
|
775
|
-
// ============================================
|
|
776
|
-
{
|
|
777
|
-
name: 'capture_screenshot',
|
|
778
|
-
description: 'Capture a screenshot of the current Roblox Studio viewport. Returns the image as base64-encoded RGBA pixel data. Use this to "see" what you\'ve built - GUIs, 3D objects, scene layout, etc. The screenshot captures exactly what\'s visible in the Studio viewport.',
|
|
779
|
-
inputSchema: {
|
|
780
|
-
type: 'object',
|
|
781
|
-
properties: {
|
|
782
|
-
maxWidth: {
|
|
783
|
-
type: 'number',
|
|
784
|
-
description: 'Maximum width of the returned image (default: 768). Smaller = faster + less data.',
|
|
785
|
-
default: 768
|
|
786
|
-
},
|
|
787
|
-
maxHeight: {
|
|
788
|
-
type: 'number',
|
|
789
|
-
description: 'Maximum height of the returned image (default: 768). Smaller = faster + less data.',
|
|
790
|
-
default: 768
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
},
|
|
795
|
-
// ============================================
|
|
796
|
-
// VIEWPORTFRAME RENDERING (Visual feedback)
|
|
797
|
-
// ============================================
|
|
798
|
-
{
|
|
799
|
-
name: 'render_object_view',
|
|
800
|
-
description: `Render an object as an image from any angle using ViewportFrame. This is the PRIMARY tool for visual feedback - use it whenever you need to "see" what you've created or verify visual appearance.
|
|
801
|
-
|
|
802
|
-
Works in ANY Studio state (Edit/Play/Run) - no CaptureService limitations!
|
|
803
|
-
Instant rendering with full control over camera, lighting, and background.
|
|
804
|
-
|
|
805
|
-
Use cases:
|
|
806
|
-
- Verify visual appearance of created objects
|
|
807
|
-
- Generate thumbnails/previews
|
|
808
|
-
- Debug positioning and orientation
|
|
809
|
-
- Iterate on visual designs
|
|
810
|
-
- Show users what their objects look like
|
|
811
|
-
|
|
812
|
-
Available camera angles: front, back, left, right, top, bottom, iso (isometric), iso_front, iso_back, low_angle, high_angle
|
|
813
|
-
Or provide custom angles with pitch/yaw/roll in degrees.
|
|
814
|
-
|
|
815
|
-
Lighting presets: bright (3-point lighting), studio (flat/even), dark (dramatic), default (ambient only)`,
|
|
816
|
-
inputSchema: {
|
|
817
|
-
type: 'object',
|
|
818
|
-
properties: {
|
|
819
|
-
instancePath: {
|
|
820
|
-
type: 'string',
|
|
821
|
-
description: 'Path to the object to render (e.g., "game.Workspace.Model1")',
|
|
822
|
-
},
|
|
823
|
-
angle: {
|
|
824
|
-
description: 'Camera angle - use preset string or custom object with pitch/yaw/roll/distance',
|
|
825
|
-
oneOf: [
|
|
826
|
-
{
|
|
827
|
-
type: 'string',
|
|
828
|
-
enum: ['front', 'back', 'left', 'right', 'top', 'bottom', 'iso', 'iso_front', 'iso_back', 'low_angle', 'high_angle'],
|
|
829
|
-
},
|
|
830
|
-
{
|
|
831
|
-
type: 'object',
|
|
832
|
-
properties: {
|
|
833
|
-
pitch: { type: 'number', description: 'Pitch angle in degrees' },
|
|
834
|
-
yaw: { type: 'number', description: 'Yaw angle in degrees' },
|
|
835
|
-
roll: { type: 'number', description: 'Roll angle in degrees' },
|
|
836
|
-
distance: { type: 'number', description: 'Camera distance from object' },
|
|
837
|
-
},
|
|
838
|
-
},
|
|
839
|
-
],
|
|
840
|
-
},
|
|
841
|
-
resolution: {
|
|
842
|
-
type: 'object',
|
|
843
|
-
properties: {
|
|
844
|
-
width: { type: 'number', description: 'Image width (64-2048, default: 768)' },
|
|
845
|
-
height: { type: 'number', description: 'Image height (64-2048, default: 768)' },
|
|
846
|
-
},
|
|
847
|
-
description: 'Render resolution',
|
|
848
|
-
},
|
|
849
|
-
lighting: {
|
|
850
|
-
type: 'string',
|
|
851
|
-
enum: ['default', 'bright', 'studio', 'dark', 'showcase', 'dramatic', 'flat'],
|
|
852
|
-
description: 'Lighting preset to use (default: bright)',
|
|
853
|
-
},
|
|
854
|
-
background: {
|
|
855
|
-
type: 'string',
|
|
856
|
-
enum: ['transparent', 'grid', 'solid'],
|
|
857
|
-
description: 'Background style (default: transparent)',
|
|
858
|
-
},
|
|
859
|
-
autoDistance: {
|
|
860
|
-
type: 'boolean',
|
|
861
|
-
description: 'Automatically calculate camera distance to fit object (default: true)',
|
|
862
|
-
},
|
|
863
|
-
},
|
|
864
|
-
required: ['instancePath'],
|
|
865
|
-
},
|
|
866
|
-
},
|
|
867
|
-
// ============================================
|
|
868
|
-
// CAMERA CONTROL (Focus Studio camera)
|
|
869
|
-
// ============================================
|
|
870
|
-
{
|
|
871
|
-
name: 'focus_camera',
|
|
872
|
-
description: `Position the Studio camera to focus on an object (like pressing F in Studio).
|
|
873
|
-
Automatically calculates distance to fit the object in view, works with any object size.
|
|
874
|
-
|
|
875
|
-
Perfect combo with capture_screenshot:
|
|
876
|
-
1. focus_camera({instancePath: "...", angle: "front"})
|
|
877
|
-
2. capture_screenshot()
|
|
878
|
-
|
|
879
|
-
Supported angles:
|
|
880
|
-
- Standard views: front, back, left, right, top, bottom
|
|
881
|
-
- Isometric: iso (default), iso_front, iso_back
|
|
882
|
-
- Dramatic: low_angle, high_angle
|
|
883
|
-
- Custom: {pitch: 30, yaw: 45, roll: 0}
|
|
884
|
-
|
|
885
|
-
Auto-sizing:
|
|
886
|
-
- Tiny objects (0.1 studs): Camera backs up to minimum 5 studs
|
|
887
|
-
- Normal objects (10 studs): Camera positioned perfectly
|
|
888
|
-
- Huge objects (1000 studs): Camera backs up far enough to see everything`,
|
|
889
|
-
inputSchema: {
|
|
890
|
-
type: 'object',
|
|
891
|
-
properties: {
|
|
892
|
-
instancePath: {
|
|
893
|
-
type: 'string',
|
|
894
|
-
description: 'Path to the object to focus on (e.g., "game.Workspace.Model1")',
|
|
895
|
-
},
|
|
896
|
-
angle: {
|
|
897
|
-
description: 'Camera angle - preset string or custom {pitch, yaw, roll}',
|
|
898
|
-
oneOf: [
|
|
899
|
-
{
|
|
900
|
-
type: 'string',
|
|
901
|
-
enum: ['front', 'back', 'left', 'right', 'top', 'bottom', 'iso', 'iso_front', 'iso_back', 'low_angle', 'high_angle'],
|
|
902
|
-
},
|
|
903
|
-
{
|
|
904
|
-
type: 'object',
|
|
905
|
-
properties: {
|
|
906
|
-
pitch: { type: 'number', description: 'Pitch angle in degrees' },
|
|
907
|
-
yaw: { type: 'number', description: 'Yaw angle in degrees' },
|
|
908
|
-
roll: { type: 'number', description: 'Roll angle in degrees' },
|
|
909
|
-
},
|
|
910
|
-
},
|
|
911
|
-
],
|
|
912
|
-
},
|
|
913
|
-
distance: {
|
|
914
|
-
type: 'number',
|
|
915
|
-
description: 'Manual camera distance (overrides auto-distance)',
|
|
916
|
-
},
|
|
917
|
-
autoDistance: {
|
|
918
|
-
type: 'boolean',
|
|
919
|
-
description: 'Automatically calculate distance to fit object (default: true)',
|
|
920
|
-
},
|
|
921
|
-
},
|
|
922
|
-
required: ['instancePath'],
|
|
923
|
-
},
|
|
924
|
-
},
|
|
925
|
-
// ============================================
|
|
926
|
-
// EXECUTE LUA TOOL (Run arbitrary Lua code)
|
|
927
|
-
// ============================================
|
|
928
|
-
{
|
|
929
|
-
name: 'execute_lua',
|
|
930
|
-
description: 'Execute arbitrary Lua/Luau code in Roblox Studio. This is a powerful tool that runs code directly in the Studio plugin context with access to all services, Instance constructors, and the full Roblox API. Use this for complex operations that would require multiple tool calls, debugging, prototyping, or any task that\'s easier to express in code. Returns the result of the last expression.',
|
|
931
|
-
inputSchema: {
|
|
932
|
-
type: 'object',
|
|
933
|
-
properties: {
|
|
934
|
-
code: {
|
|
935
|
-
type: 'string',
|
|
936
|
-
description: 'The Lua/Luau code to execute. Has access to: game, workspace, all services (Players, ReplicatedStorage, etc.), Instance constructors (Vector3, CFrame, Color3, etc.), and helper function getInstanceByPath(path). Return a value to get it back in the response.'
|
|
937
|
-
}
|
|
938
|
-
},
|
|
939
|
-
required: ['code']
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
]
|
|
943
|
-
};
|
|
944
|
-
});
|
|
46
|
+
// List tools — strip handlers, expose only MCP-visible fields.
|
|
47
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
48
|
+
tools: allTools.map(({ name, description, inputSchema }) => ({
|
|
49
|
+
name,
|
|
50
|
+
description,
|
|
51
|
+
inputSchema,
|
|
52
|
+
})),
|
|
53
|
+
}));
|
|
54
|
+
// Dispatch a single tool call via the registry.
|
|
945
55
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
946
56
|
const { name, arguments: args } = request.params;
|
|
57
|
+
const tool = toolsByName[name];
|
|
58
|
+
if (!tool) {
|
|
59
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
60
|
+
}
|
|
947
61
|
try {
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
return await this.tools.getPlaceInfo();
|
|
952
|
-
case 'get_services':
|
|
953
|
-
return await this.tools.getServices(args?.serviceName);
|
|
954
|
-
// Property & Instance Tools
|
|
955
|
-
case 'get_instance_properties':
|
|
956
|
-
return await this.tools.getInstanceProperties(args?.instancePath);
|
|
957
|
-
case 'get_class_info':
|
|
958
|
-
return await this.tools.getClassInfo(args?.className);
|
|
959
|
-
// Project Tools
|
|
960
|
-
case 'get_project_structure':
|
|
961
|
-
return await this.tools.getProjectStructure(args?.path, args?.maxDepth, args?.scriptsOnly);
|
|
962
|
-
// Property Modification Tools
|
|
963
|
-
case 'set_property':
|
|
964
|
-
return await this.tools.setProperty(args?.instancePath, args?.propertyName, args?.propertyValue);
|
|
965
|
-
// Mass Property Tools
|
|
966
|
-
case 'mass_set_property':
|
|
967
|
-
return await this.tools.massSetProperty(args?.paths, args?.propertyName, args?.propertyValue);
|
|
968
|
-
case 'mass_get_property':
|
|
969
|
-
return await this.tools.massGetProperty(args?.paths, args?.propertyName);
|
|
970
|
-
// Object Creation/Deletion Tools
|
|
971
|
-
case 'create_object':
|
|
972
|
-
return await this.tools.createObject(args?.className, args?.parent, args?.name);
|
|
973
|
-
case 'delete_object':
|
|
974
|
-
return await this.tools.deleteObject(args?.instancePath);
|
|
975
|
-
// Script Management Tools
|
|
976
|
-
case 'get_script_source':
|
|
977
|
-
return await this.tools.getScriptSource(args?.instancePath, args?.startLine, args?.endLine);
|
|
978
|
-
case 'set_script_source':
|
|
979
|
-
return await this.tools.setScriptSource(args?.instancePath, args?.source);
|
|
980
|
-
// Partial Script Editing Tools (line-based - legacy)
|
|
981
|
-
case 'edit_script_lines':
|
|
982
|
-
return await this.tools.editScriptLines(args?.instancePath, args?.startLine, args?.endLine, args?.newContent);
|
|
983
|
-
case 'insert_script_lines':
|
|
984
|
-
return await this.tools.insertScriptLines(args?.instancePath, args?.afterLine, args?.newContent);
|
|
985
|
-
case 'delete_script_lines':
|
|
986
|
-
return await this.tools.deleteScriptLines(args?.instancePath, args?.startLine, args?.endLine);
|
|
987
|
-
// Claude Code-Style Script Editing Tools (RECOMMENDED)
|
|
988
|
-
case 'edit_script':
|
|
989
|
-
return await this.tools.editScript(args?.instancePath, args?.old_string, args?.new_string, args?.replace_all ?? false, args?.validate_after ?? true);
|
|
990
|
-
case 'search_script':
|
|
991
|
-
return await this.tools.searchScript(args?.instancePath, args?.pattern, args?.use_regex ?? false, args?.context_lines ?? 0);
|
|
992
|
-
case 'get_script_function':
|
|
993
|
-
return await this.tools.getScriptFunction(args?.instancePath, args?.function_name);
|
|
994
|
-
case 'find_and_replace_in_scripts':
|
|
995
|
-
return await this.tools.findAndReplaceInScripts(args?.paths, args?.old_string, args?.new_string, args?.validate_after ?? true);
|
|
996
|
-
// Attribute Tools
|
|
997
|
-
case 'get_attribute':
|
|
998
|
-
return await this.tools.getAttribute(args?.instancePath, args?.attributeName);
|
|
999
|
-
case 'set_attribute':
|
|
1000
|
-
return await this.tools.setAttribute(args?.instancePath, args?.attributeName, args?.attributeValue, args?.valueType);
|
|
1001
|
-
case 'get_attributes':
|
|
1002
|
-
return await this.tools.getAttributes(args?.instancePath);
|
|
1003
|
-
case 'delete_attribute':
|
|
1004
|
-
return await this.tools.deleteAttribute(args?.instancePath, args?.attributeName);
|
|
1005
|
-
// Tag Tools (CollectionService)
|
|
1006
|
-
case 'get_tags':
|
|
1007
|
-
return await this.tools.getTags(args?.instancePath);
|
|
1008
|
-
case 'add_tag':
|
|
1009
|
-
return await this.tools.addTag(args?.instancePath, args?.tagName);
|
|
1010
|
-
case 'remove_tag':
|
|
1011
|
-
return await this.tools.removeTag(args?.instancePath, args?.tagName);
|
|
1012
|
-
case 'get_tagged':
|
|
1013
|
-
return await this.tools.getTagged(args?.tagName);
|
|
1014
|
-
// Selection Tools
|
|
1015
|
-
case 'get_selection':
|
|
1016
|
-
return await this.tools.getSelection();
|
|
1017
|
-
// Output Capture Tool
|
|
1018
|
-
case 'get_output':
|
|
1019
|
-
return await this.tools.getOutput(args?.limit, args?.since, args?.messageTypes, args?.clear);
|
|
1020
|
-
// Instance Manipulation Tools
|
|
1021
|
-
case 'clone_instance':
|
|
1022
|
-
return await this.tools.cloneInstance(args?.sourcePath, args?.targetParent, args?.newName);
|
|
1023
|
-
case 'move_instance':
|
|
1024
|
-
return await this.tools.moveInstance(args?.instancePath, args?.newParent);
|
|
1025
|
-
// Script Validation Tool
|
|
1026
|
-
case 'validate_script':
|
|
1027
|
-
return await this.tools.validateScript(args?.instancePath, args?.source);
|
|
1028
|
-
// Undo/Redo Tools
|
|
1029
|
-
case 'undo':
|
|
1030
|
-
return await this.tools.undo();
|
|
1031
|
-
case 'redo':
|
|
1032
|
-
return await this.tools.redo();
|
|
1033
|
-
// Asset Insertion Tool
|
|
1034
|
-
case 'insert_asset':
|
|
1035
|
-
return await this.tools.insertAsset(args?.assetId, args?.folderName, args?.targetParent);
|
|
1036
|
-
// Playtest Control Tools
|
|
1037
|
-
case 'play_solo':
|
|
1038
|
-
return await this.tools.playSolo();
|
|
1039
|
-
case 'stop_play':
|
|
1040
|
-
return await this.tools.stopPlay();
|
|
1041
|
-
case 'get_playtest_output':
|
|
1042
|
-
return await this.tools.getPlaytestOutput(args?.sinceSeq, args?.limit, args?.messageTypes);
|
|
1043
|
-
// Screenshot Tool
|
|
1044
|
-
case 'capture_screenshot':
|
|
1045
|
-
return await this.tools.captureScreenshot(args?.maxWidth, args?.maxHeight);
|
|
1046
|
-
// ViewportFrame Rendering
|
|
1047
|
-
case 'render_object_view':
|
|
1048
|
-
return await this.tools.renderObjectView(args?.instancePath, {
|
|
1049
|
-
angle: args?.angle,
|
|
1050
|
-
resolution: args?.resolution,
|
|
1051
|
-
lighting: args?.lighting,
|
|
1052
|
-
background: args?.background,
|
|
1053
|
-
autoDistance: args?.autoDistance,
|
|
1054
|
-
});
|
|
1055
|
-
// Camera Control
|
|
1056
|
-
case 'focus_camera':
|
|
1057
|
-
return await this.tools.focusCamera(args?.instancePath, {
|
|
1058
|
-
angle: args?.angle,
|
|
1059
|
-
distance: args?.distance,
|
|
1060
|
-
autoDistance: args?.autoDistance,
|
|
1061
|
-
});
|
|
1062
|
-
// Execute Lua Tool
|
|
1063
|
-
case 'execute_lua':
|
|
1064
|
-
return await this.tools.executeLua(args?.code);
|
|
1065
|
-
default:
|
|
1066
|
-
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
1067
|
-
}
|
|
62
|
+
const result = await tool.handler(args ?? {}, { tools: this.tools });
|
|
63
|
+
// Append the tool's just-in-time steering (no-op if it has none).
|
|
64
|
+
return applyNudge(result, tool.nudge);
|
|
1068
65
|
}
|
|
1069
66
|
catch (error) {
|
|
1070
67
|
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1072,7 +69,9 @@ Auto-sizing:
|
|
|
1072
69
|
});
|
|
1073
70
|
}
|
|
1074
71
|
async run() {
|
|
1075
|
-
const port = process.env.ROBLOX_STUDIO_PORT
|
|
72
|
+
const port = process.env.ROBLOX_STUDIO_PORT
|
|
73
|
+
? parseInt(process.env.ROBLOX_STUDIO_PORT)
|
|
74
|
+
: 3002;
|
|
1076
75
|
const host = process.env.ROBLOX_STUDIO_HOST || '0.0.0.0';
|
|
1077
76
|
const httpServer = createHttpServer(this.tools, this.bridge);
|
|
1078
77
|
await new Promise((resolve) => {
|
|
@@ -1086,11 +85,12 @@ Auto-sizing:
|
|
|
1086
85
|
console.error('Roblox Studio MCP server running on stdio');
|
|
1087
86
|
httpServer.setMCPServerActive(true);
|
|
1088
87
|
console.error('MCP server marked as active');
|
|
1089
|
-
console.error(
|
|
88
|
+
console.error(`Registered ${allTools.length} tools, waiting for Studio plugin...`);
|
|
1090
89
|
setInterval(() => {
|
|
1091
90
|
const pluginConnected = httpServer.isPluginConnected();
|
|
1092
91
|
const mcpActive = httpServer.isMCPServerActive();
|
|
1093
92
|
if (pluginConnected && mcpActive) {
|
|
93
|
+
// both up; quiet
|
|
1094
94
|
}
|
|
1095
95
|
else if (pluginConnected && !mcpActive) {
|
|
1096
96
|
console.error('Studio plugin connected, but MCP server inactive');
|