import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from 'zod';
import { BridgeManager } from './bridge.js';
// --- Bridge Setup ---
const bridge = new BridgeManager();
const PORT = 3000;
bridge.startServer(PORT);
// --- MCP Setup ---
const server = new Server(
{
name: "roblox-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Define Tool Schemas
const ListChildrenSchema = z.object({
path: z.string().default("game").describe("The path to the instance to list children of (e.g., 'game.Workspace')"),
});
const GetPropertiesSchema = z.object({
path: z.string().describe("The path to the instance"),
properties: z.array(z.string()).optional().describe("List of property names to fetch. If omitted, fetches standard properties."),
});
const ReadScriptSchema = z.object({
path: z.string().describe("The path to the script to read"),
});
const RunScriptSchema = z.object({
code: z.string().describe("The Lua code to execute"),
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "list_children",
description: "List children of a Roblox instance",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "The path to the instance to list children of (e.g., 'game.Workspace')",
default: "game"
}
}
},
},
{
name: "get_properties",
description: "Get properties of a Roblox instance",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "The path to the instance"
},
properties: {
type: "array",
items: { type: "string" },
description: "List of property names to fetch. If omitted, fetches standard properties."
}
},
required: ["path"]
},
},
{
name: "read_script",
description: "Read the source code of a script (decompiles if necessary). Note: Cannot read server-side scripts (Script) from a client execution context as their source is not replicated.",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "The path to the script to read"
}
},
required: ["path"]
},
},
{
name: "run_script",
description: "Execute arbitrary Lua code in the Roblox client",
inputSchema: {
type: "object",
properties: {
code: {
type: "string",
description: "The Lua code to execute"
}
},
required: ["code"]
},
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case "list_children": {
// @ts-ignore
const { path } = ListChildrenSchema.parse(request.params.arguments || {});
const result = await bridge.enqueueCommand("list_children", { path });
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
case "get_properties": {
// @ts-ignore
const { path, properties } = GetPropertiesSchema.parse(request.params.arguments);
const result = await bridge.enqueueCommand("get_properties", { path, properties });
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
case "read_script": {
const { path } = ReadScriptSchema.parse(request.params.arguments);
const result = await bridge.enqueueCommand("read_script", { path });
return {
content: [{ type: "text", text: String(result) }],
};
}
case "run_script": {
const { code } = RunScriptSchema.parse(request.params.arguments);
const result = await bridge.enqueueCommand("run_script", { code });
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
default:
throw new Error("Unknown tool");
}
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error(`Invalid arguments: ${error.message}`);
}
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [{ type: "text", text: `Error: ${errorMessage}` }],
isError: true,
};
}
});
async function run() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Roblox MCP Server running on stdio");
}
run().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});