roblox-mcp-difz 1.0.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 ADDED
Binary file
package/bin/roblox-mcp ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ // roblox-mcp CLI entry point
4
+ // Referenced by "bin" in package.json
5
+ require('../dist/cli.js');
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /**
5
+ * cli.ts — CLI dispatcher for roblox-mcp-difz
6
+ *
7
+ * Commands:
8
+ * roblox-mcp-difz → start HTTP server
9
+ * roblox-mcp-difz start → start HTTP server
10
+ * roblox-mcp-difz start:stdio → start stdio MCP transport + HTTP
11
+ * roblox-mcp-difz setup → interactive setup wizard
12
+ * roblox-mcp-difz setup --ai <name> → auto-setup for specific AI
13
+ */
14
+ const path = require('path');
15
+ const readline = require('readline');
16
+ function printBanner(port, toolsCount, mode, wsCount) {
17
+ console.log('╔══════════════════════════════════════════════════════╗');
18
+ console.log('║ Roblox MCP Server v1.0.0 ║');
19
+ console.log('╠══════════════════════════════════════════════════════╣');
20
+ console.log(`║ HTTP Server : http://localhost:${port} ║`);
21
+ console.log(`║ MCP Endpoint: POST http://localhost:${port}/mcp ║`);
22
+ console.log(`║ WS Endpoint : ws://localhost:${port}/ws ║`);
23
+ console.log(`║ Client Script: http://localhost:${port}/mcp.luau ║`);
24
+ console.log(`║ Tools : ${String(toolsCount).padStart(2)} registered ║`);
25
+ console.log(`║ Mode : ${mode.padEnd(20)} ║`);
26
+ console.log('╚══════════════════════════════════════════════════════╝');
27
+ }
28
+ async function cmdStart(stdioMode) {
29
+ const { createApp } = require('./server-core');
30
+ const PORT = parseInt(process.env.MCP_PORT, 10) || 28429;
31
+ if (stdioMode) {
32
+ const orig = console.log;
33
+ console.log = function (...args) {
34
+ process.stderr.write('[MCP] ' + args.join(' ') + '\n');
35
+ };
36
+ }
37
+ const { app, server, tools, mcp, wss } = createApp({ stdio: stdioMode });
38
+ server.listen(PORT, () => {
39
+ const mode = stdioMode ? 'HTTP + stdio MCP + WS' : 'HTTP + WS';
40
+ printBanner(PORT, tools.count, mode, wss ? wss.connectedCount : 0);
41
+ if (stdioMode) {
42
+ console.log('[MCP] stdio transport active — waiting for JSON-RPC messages on stdin…');
43
+ const rl = readline.createInterface({ input: process.stdin });
44
+ rl.on('line', async (line) => {
45
+ const trimmed = line.trim();
46
+ if (!trimmed)
47
+ return;
48
+ let message;
49
+ try {
50
+ message = JSON.parse(trimmed);
51
+ }
52
+ catch {
53
+ return;
54
+ }
55
+ if (message.id === undefined || message.id === null) {
56
+ try {
57
+ await mcp.handleMessage(message);
58
+ }
59
+ catch { }
60
+ return;
61
+ }
62
+ try {
63
+ const result = await mcp.handleMessage(message);
64
+ process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: message.id, ...result }) + '\n');
65
+ }
66
+ catch (err) {
67
+ process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: message.id, error: { code: -32603, message: err.message } }) + '\n');
68
+ }
69
+ });
70
+ rl.on('close', () => process.exit(0));
71
+ }
72
+ });
73
+ }
74
+ async function cmdSetup(targetAI) {
75
+ const { runSetupWizard } = require('./setup');
76
+ await runSetupWizard(targetAI);
77
+ }
78
+ function cmdHelp() {
79
+ console.log(`
80
+ roblox-mcp-difz — Roblox MCP Server
81
+
82
+ USAGE:
83
+ roblox-mcp-difz Start HTTP server (port 28429)
84
+ roblox-mcp-difz start Start HTTP server
85
+ roblox-mcp-difz start:stdio Start stdio MCP transport + HTTP
86
+ roblox-mcp-difz setup Interactive setup wizard
87
+ roblox-mcp-difz setup --ai <name> Setup for specific AI (claude-code, claude-desktop, cursor, windsurf, generic)
88
+ roblox-mcp-difz --help Show this help
89
+
90
+ ENV:
91
+ MCP_PORT Port for HTTP server (default: 28429)
92
+ `);
93
+ }
94
+ async function main() {
95
+ const args = process.argv.slice(2);
96
+ const cmd = args[0];
97
+ if (cmd === '--help' || cmd === '-h' || cmd === 'help') {
98
+ cmdHelp();
99
+ return;
100
+ }
101
+ if (cmd === 'setup') {
102
+ const aiFlag = args.indexOf('--ai');
103
+ const targetAI = aiFlag !== -1 ? args[aiFlag + 1] : null;
104
+ await cmdSetup(targetAI);
105
+ return;
106
+ }
107
+ if (cmd === 'start:stdio' || args.includes('--stdio')) {
108
+ await cmdStart(true);
109
+ return;
110
+ }
111
+ await cmdStart(false);
112
+ }
113
+ if (require.main === module) {
114
+ main().catch((err) => {
115
+ console.error('[roblox-mcp-difz] Fatal:', err.message);
116
+ process.exit(1);
117
+ });
118
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * index.ts — Programmatic entry for roblox-mcp
3
+ *
4
+ * Usage:
5
+ * const robloxMcp = require('roblox-mcp-difz');
6
+ * const { app, queue, tools } = robloxMcp.createApp({ verbose: true });
7
+ * app.listen(28429);
8
+ *
9
+ * console.log('Tools:', robloxMcp.getTools().length);
10
+ */
11
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ /**
3
+ * index.ts — Programmatic entry for roblox-mcp
4
+ *
5
+ * Usage:
6
+ * const robloxMcp = require('roblox-mcp-difz');
7
+ * const { app, queue, tools } = robloxMcp.createApp({ verbose: true });
8
+ * app.listen(28429);
9
+ *
10
+ * console.log('Tools:', robloxMcp.getTools().length);
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const { createApp } = require('./server-core');
14
+ const { ToolDefinitions } = require('./tool-definitions');
15
+ const { QueueManager } = require('./queue-manager');
16
+ const { SessionManager } = require('./session-manager');
17
+ const { WsServer } = require('./ws-server');
18
+ const processManager = require('./process-manager');
19
+ function getTools() {
20
+ return new ToolDefinitions().getTools();
21
+ }
22
+ module.exports = {
23
+ createApp,
24
+ getTools,
25
+ ToolDefinitions,
26
+ QueueManager,
27
+ SessionManager,
28
+ WsServer,
29
+ processManager,
30
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * mcp-handler.ts
3
+ *
4
+ * MCP (Model Context Protocol) JSON-RPC 2.0 message handler.
5
+ *
6
+ * Server-side tools (get_roblox_processes, launch_roblox, open_game, etc.)
7
+ * are executed directly on Node — not sent to the executor queue.
8
+ */
9
+ export {};
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ /**
3
+ * mcp-handler.ts
4
+ *
5
+ * MCP (Model Context Protocol) JSON-RPC 2.0 message handler.
6
+ *
7
+ * Server-side tools (get_roblox_processes, launch_roblox, open_game, etc.)
8
+ * are executed directly on Node — not sent to the executor queue.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ /** Tools that run on the Node server directly, not via executor */
12
+ const SERVER_SIDE_TOOLS = new Set([
13
+ 'get_roblox_processes',
14
+ 'launch_roblox',
15
+ 'open_game',
16
+ 'capture_roblox_screenshot',
17
+ 'get_roblox_versions',
18
+ ]);
19
+ class McpHandler {
20
+ /**
21
+ * @param queue - The task queue manager
22
+ * @param tools - Tool definitions registry
23
+ * @param sessions - Session manager
24
+ * @param processManager - Process manager module
25
+ */
26
+ constructor(queue, tools, sessions, processManager) {
27
+ this.queue = queue;
28
+ this.tools = tools;
29
+ this.sessions = sessions;
30
+ this.proc = processManager;
31
+ this.serverInfo = {
32
+ name: 'roblox-mcp-difz-server',
33
+ version: '1.0.0',
34
+ description: 'Roblox MCP — full game control, reverse engineering, ' +
35
+ 'and exploitation framework via Model Context Protocol.',
36
+ };
37
+ this.initialized = false;
38
+ }
39
+ async handleMessage(message) {
40
+ const { method, params } = message;
41
+ if (!method) {
42
+ return { error: { code: -32600, message: 'Invalid Request: method is required' } };
43
+ }
44
+ switch (method) {
45
+ case 'initialize':
46
+ return this._handleInitialize(params);
47
+ case 'shutdown':
48
+ return this._handleShutdown();
49
+ case 'notifications/initialized':
50
+ return { result: { acknowledged: true } };
51
+ case 'tools/list':
52
+ return this._handleToolsList();
53
+ case 'tools/call':
54
+ return await this._handleToolsCall(params);
55
+ case 'resources/list':
56
+ return this._handleResourcesList();
57
+ case 'resources/read':
58
+ return await this._handleResourcesRead(params);
59
+ case 'prompts/list':
60
+ return this._handlePromptsList();
61
+ case 'ping':
62
+ return { result: { status: 'pong', timestamp: Date.now(), stats: this.queue.getStats() } };
63
+ case 'mcp/setup':
64
+ return this._handleSetup();
65
+ default:
66
+ return { error: { code: -32601, message: `Method not found: ${method}` } };
67
+ }
68
+ }
69
+ _handleInitialize(_params) {
70
+ this.initialized = true;
71
+ return {
72
+ result: {
73
+ protocolVersion: '2024-11-05',
74
+ capabilities: {
75
+ tools: { listChanged: false },
76
+ resources: { listChanged: false, subscribe: false },
77
+ prompts: {},
78
+ },
79
+ serverInfo: this.serverInfo,
80
+ },
81
+ };
82
+ }
83
+ _handleShutdown() {
84
+ this.initialized = false;
85
+ return { result: { success: true, message: 'Server shutting down' } };
86
+ }
87
+ _handleToolsList() {
88
+ return { result: { tools: this.tools.getTools() } };
89
+ }
90
+ async _handleToolsCall(params) {
91
+ const { name, arguments: args } = (params || {});
92
+ if (!name) {
93
+ return { error: { code: -32602, message: 'Tool name is required' } };
94
+ }
95
+ const tool = this.tools.getTool(name);
96
+ if (!tool) {
97
+ return { error: { code: -32602, message: `Unknown tool: ${name}` } };
98
+ }
99
+ try {
100
+ // Server-side tools run on Node directly
101
+ if (SERVER_SIDE_TOOLS.has(name)) {
102
+ const result = this._runServerTool(name, args || {});
103
+ return {
104
+ result: {
105
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
106
+ meta: { tool: name, execution: 'server' },
107
+ },
108
+ };
109
+ }
110
+ // Executor tools — queue for executor, with optional workerId targeting
111
+ const startTime = Date.now();
112
+ const workerId = (args && args.pid) ? String(args.pid) : undefined;
113
+ const result = await this.queue.submitTask(name, args || {}, { workerId });
114
+ const elapsed = Date.now() - startTime;
115
+ return {
116
+ result: {
117
+ content: [{
118
+ type: 'text',
119
+ text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
120
+ }],
121
+ meta: { executionTimeMs: elapsed, tool: name },
122
+ },
123
+ };
124
+ }
125
+ catch (err) {
126
+ const errorMessage = err instanceof Error ? err.message : String(err);
127
+ return {
128
+ result: {
129
+ content: [{ type: 'text', text: JSON.stringify({ success: false, error: errorMessage }) }],
130
+ isError: true,
131
+ meta: { tool: name, error: errorMessage },
132
+ },
133
+ };
134
+ }
135
+ }
136
+ _runServerTool(name, args) {
137
+ switch (name) {
138
+ case 'get_roblox_processes':
139
+ return { success: true, processes: this.proc.listRobloxProcesses(), count: this.sessions.activeCount };
140
+ case 'launch_roblox':
141
+ return this.proc.launchRoblox(args.path || null);
142
+ case 'open_game':
143
+ return this.proc.openGame(args.place_id, {
144
+ jobId: args.job_id,
145
+ privateServerLinkCode: args.private_server_link_code,
146
+ browserTrackerId: args.browser_tracker_id,
147
+ launchTime: args.launch_time,
148
+ launchMode: args.launch_mode,
149
+ authTicket: args.auth_ticket,
150
+ experienceId: args.experience_id,
151
+ });
152
+ case 'capture_roblox_screenshot':
153
+ return this.proc.captureRobloxWindow(args.pid || null);
154
+ case 'get_roblox_versions':
155
+ return this._getRobloxVersions();
156
+ default:
157
+ return { success: false, error: `Unknown server tool: ${name}` };
158
+ }
159
+ }
160
+ _getRobloxVersions() {
161
+ const fs = require('fs');
162
+ const path = require('path');
163
+ const versions = [];
164
+ const candidates = [
165
+ process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Roblox', 'Versions') : '',
166
+ 'C:\\Program Files (x86)\\Roblox\\Versions',
167
+ 'C:\\Program Files\\Roblox\\Versions',
168
+ ];
169
+ for (const dir of candidates) {
170
+ if (!dir || !fs.existsSync(dir))
171
+ continue;
172
+ try {
173
+ const entries = fs.readdirSync(dir).filter((v) => v.startsWith('version-')).sort().reverse();
174
+ for (const ver of entries) {
175
+ const launcher = path.join(dir, ver, 'RobloxPlayerLauncher.exe');
176
+ const player = path.join(dir, ver, 'RobloxPlayerBeta.exe');
177
+ versions.push({
178
+ version: ver.replace('version-', ''),
179
+ path: dir + '/' + ver,
180
+ hasPlayerLauncher: fs.existsSync(launcher),
181
+ hasPlayerBeta: fs.existsSync(player),
182
+ });
183
+ }
184
+ }
185
+ catch {
186
+ // ignore inaccessible directories
187
+ }
188
+ }
189
+ return { success: true, versions };
190
+ }
191
+ _handleResourcesList() {
192
+ return {
193
+ result: {
194
+ resources: [
195
+ { uri: 'mcp://roblox/game/metadata', name: 'Game Metadata', description: 'Current game session metadata', mimeType: 'application/json' },
196
+ { uri: 'mcp://roblox/game/players', name: 'Active Players', description: 'Real-time player data', mimeType: 'application/json' },
197
+ { uri: 'mcp://roblox/game/remotes', name: 'Remote Events & Functions', description: 'All detected remotes', mimeType: 'application/json' },
198
+ { uri: 'mcp://roblox/game/workspace', name: 'Workspace Objects', description: '3D object tree', mimeType: 'application/json' },
199
+ { uri: 'mcp://roblox/game/console', name: 'Console Logs', description: 'Recent LogService output', mimeType: 'application/json' },
200
+ ],
201
+ },
202
+ };
203
+ }
204
+ async _handleResourcesRead(params) {
205
+ const { uri } = (params || {});
206
+ if (!uri)
207
+ return { error: { code: -32602, message: 'Resource URI is required' } };
208
+ const resourceMap = {
209
+ 'mcp://roblox/game/metadata': 'get_game_metadata',
210
+ 'mcp://roblox/game/players': 'dump_workspace_players',
211
+ 'mcp://roblox/game/remotes': 'dump_remote_events',
212
+ 'mcp://roblox/game/workspace': 'get_workspace_objects',
213
+ 'mcp://roblox/game/console': 'get_console_logs',
214
+ };
215
+ const toolName = resourceMap[uri];
216
+ if (!toolName)
217
+ return { error: { code: -32602, message: `Unknown resource: ${uri}` } };
218
+ try {
219
+ const result = await this.queue.submitTask(toolName, {});
220
+ return {
221
+ result: {
222
+ contents: [{
223
+ uri,
224
+ mimeType: 'application/json',
225
+ text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
226
+ }],
227
+ },
228
+ };
229
+ }
230
+ catch (err) {
231
+ const errorMessage = err instanceof Error ? err.message : String(err);
232
+ return { error: { code: -32603, message: `Failed to read resource: ${errorMessage}` } };
233
+ }
234
+ }
235
+ _handlePromptsList() {
236
+ return {
237
+ result: {
238
+ prompts: [
239
+ { name: 'analyze_game', description: 'Dumps game metadata, remotes, and player data in one shot.', arguments: [] },
240
+ { name: 'find_exploit_vector', description: 'Scan remotes and workspace to find exploit entry points.', arguments: [] },
241
+ ],
242
+ },
243
+ };
244
+ }
245
+ _handleSetup() {
246
+ return {
247
+ result: {
248
+ config: {
249
+ type: 'url',
250
+ url: 'http://localhost:28429/mcp',
251
+ name: 'Roblox MCP Difz',
252
+ description: 'Full game control & exploitation framework',
253
+ },
254
+ },
255
+ };
256
+ }
257
+ }
258
+ module.exports = { McpHandler };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * process-manager.ts
3
+ *
4
+ * Roblox process management — runs on Node side, not via executor queue.
5
+ *
6
+ * Provides:
7
+ * listRobloxProcesses() — scan OS for RobloxPlayerBeta processes
8
+ * launchRoblox(path) — spawn RobloxPlayerLauncher.exe
9
+ * openGame(placeId, opts) — open game via roblox-player protocol with full join URL
10
+ * captureRobloxWindow(pid) — capture screenshot of a Roblox process window
11
+ */
12
+ export {};