Skip to main content
The MCP server is the interface layer that exposes RbxGenie tools to AI assistants like Claude Desktop and Cursor through the Model Context Protocol (MCP).

Overview

Location: src/mcp.ts The MCP server is a stdio-based MCP implementation that:
  1. Defines 50+ tools with Zod schema validation
  2. Forwards tool calls to the Node.js daemon via HTTP
  3. Translates responses back to MCP format for AI consumption
MCP is an open protocol developed by Anthropic that allows AI assistants to interact with external systems through a standardized tool interface. Learn more at modelcontextprotocol.io

Configuration

Location: src/mcp.ts:5-6
const DAEMON_URL = process.env.DAEMON_URL || "http://127.0.0.1:7766";
const TOOL_TIMEOUT_MS = 120_000;
http://127.0.0.1:7766 — Standard daemon address

Tool Definitions

Location: src/mcp.ts:8-80 Tools are defined with name, description, and Zod schema for argument validation:
interface ToolDef {
  name: string;
  description: string;
  schema: Record<string, z.ZodTypeAny>;
}

const TOOLS: ToolDef[] = [
  {
    name: "get_file_tree",
    description: "Get the file/instance tree of the place. Args: { root?: string, depth?: number }",
    schema: {
      root: z.string().optional(),
      depth: z.number().optional()
    }
  },
  {
    name: "set_property",
    description: "Set a property on an instance. Args: { path: string, property: string, value: any }",
    schema: {
      path: z.string(),
      property: z.string(),
      value: z.any()
    }
  },
  // ... 48 more tools
];

Tool Categories

The 50+ tools are organized into logical groups:
  • get_file_tree — Recursive instance tree
  • search_files — Name pattern search
  • get_place_info — PlaceId, version, creator info
  • get_services — List all services
  • search_objects — Multi-criteria search
  • get_instance_properties — All properties of an instance
  • get_instance_children — Direct children
  • search_by_property — Find instances by property value
  • get_class_info — Roblox class information
  • get_project_structure — High-level structure
  • summarize_game — Game summary
  • set_property — Set single property
  • mass_set_property — Set property on multiple instances
  • mass_get_property — Get property from multiple instances
  • set_calculated_property — Use expression for value
  • set_relative_property — Relative adjustment
  • create_object — Create single instance
  • create_object_with_properties — Create with initial properties
  • mass_create_objects — Create multiple instances
  • mass_create_objects_with_properties — Batch create with properties
  • delete_object — Delete instance
  • smart_duplicate — Intelligent duplication
  • mass_duplicate — Duplicate multiple instances
  • get_script_source — Read script source
  • set_script_source — Replace entire source
  • edit_script_lines — Edit line range
  • insert_script_lines — Insert at line number
  • delete_script_lines — Delete line range
  • get_attribute — Get single attribute
  • set_attribute — Set attribute value
  • get_attributes — Get all attributes
  • delete_attribute — Remove attribute
  • get_tags — Get tags on instance
  • add_tag — Add CollectionService tag
  • remove_tag — Remove tag
  • get_tagged — Find all instances with tag
  • get_selection — Get currently selected instances
  • execute_luau — Run Luau code in edit mode
  • get_console_output — Retrieve console logs
  • clear_console_output — Clear log buffer
  • start_play — Enter play mode
  • stop_play — Exit play mode
  • run_server — Enter server mode
  • get_studio_mode — Current mode
  • run_script_in_play_mode — Automated test runner
  • insert_model — Search and insert free models

HTTP Proxy

Location: src/mcp.ts:82-107 The MCP server forwards tool calls to the daemon via HTTP POST:
async function callDaemonTool(name: string, args: Record<string, unknown>): Promise<string> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), TOOL_TIMEOUT_MS);

  try {
    const res = await fetch(`${DAEMON_URL}/tool/${name}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(args),
      signal: controller.signal,
    });
    const data: any = await res.json();
    if (!data.ok) {
      const msg = data.error || "Unknown error";
      return JSON.stringify({ error: msg, timeout: data.timeout });
    }
    return typeof data.result === "string" ? data.result : JSON.stringify(data.result);
  } catch (err: any) {
    if (err.name === "AbortError") {
      return JSON.stringify({ error: "MCP proxy timeout", timeout: true });
    }
    throw err;
  } finally {
    clearTimeout(timer);
  }
}
1

AbortController Setup

Create an AbortController with 120-second timeout
2

HTTP POST

POST to /tool/{name} with JSON args
3

Error Handling

If daemon returns ok: false, format error as JSON string
4

Timeout Handling

If AbortController fires, return timeout error
5

Response Formatting

Return result as string (JSON or plain text)

Server Registration

Location: src/mcp.ts:109-143
async function main() {
  const server = new McpServer({
    name: "RbxGenie",
    version: "1.0.0",
  });

  for (const def of TOOLS) {
    const shape = def.schema as Record<string, z.ZodTypeAny>;
    server.tool(
      def.name,
      def.description,
      shape,
      async (args: Record<string, unknown>) => {
        try {
          const result = await callDaemonTool(def.name, args);
          return { content: [{ type: "text" as const, text: result }] };
        } catch (err) {
          return {
            content: [{ type: "text" as const, text: `Error: ${String(err)}` }],
            isError: true,
          };
        }
      }
    );
  }

  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("[RbxGenie MCP] Server started on stdio");
}

main().catch((err) => {
  console.error("[RbxGenie MCP] Fatal:", err);
  process.exit(1);
});
The MCP server uses stdio transport, which means it communicates with the AI assistant via standard input/output. This is the standard transport for MCP servers.

Integration with AI Assistants

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):
{
  "mcpServers": {
    "rbxgenie": {
      "command": "node",
      "args": ["/path/to/RbxGenie/.build/mcp.js"]
    }
  }
}

Cursor

Add to .cursorrules or Cursor settings:
{
  "mcp": {
    "servers": {
      "rbxgenie": {
        "command": "node",
        "args": ["/path/to/RbxGenie/.build/mcp.js"]
      }
    }
  }
}

OpenCode

Add to ~/.config/opencode/mcp.json:
{
  "mcpServers": {
    "rbxgenie": {
      "command": "node",
      "args": ["/path/to/RbxGenie/.build/mcp.js"]
    }
  }
}
Once configured, the AI assistant will automatically discover all 50+ RbxGenie tools and can use them in conversations.

Tool Response Format

All tools return text responses formatted for AI consumption:

Success Response

Simple text or JSON string:
Property set
or
{
  "name": "Part",
  "className": "Part",
  "children": [
    { "name": "Mesh", "className": "SpecialMesh" }
  ]
}

Error Response

JSON with error field:
{
  "error": "Path not found at segment [2] 'Part' in 'Workspace.Part'"
}

Timeout Response

{
  "error": "MCP proxy timeout",
  "timeout": true
}
The MCP server always returns content: [{ type: "text", text: "..." }] format. Complex data structures are JSON-stringified for the AI to parse.

Error Propagation

Errors flow through multiple layers:
1

Plugin Error

Lua error or validation failure in tool execution
2

Bridge Error

Posted to /result with error field
3

Daemon Error

Returned as { ok: false, error: "..." } in HTTP response
4

MCP Proxy Error

Formatted as JSON string with error field
5

MCP Response Error

Returned with isError: true flag to AI assistant
The AI assistant receives errors as text responses. It’s responsible for parsing error JSON and determining how to handle failures (retry, report to user, etc.).

Debugging

The MCP server logs to stderr (visible in AI assistant logs):
console.error("[RbxGenie MCP] Server started on stdio");
console.error("[RbxGenie MCP] Fatal:", err);
Check the AI assistant’s logs for MCP server errors:Claude Desktop:
  • macOS: ~/Library/Logs/Claude/mcp*.log
  • Windows: %APPDATA%\Claude\logs\mcp*.log
Cursor:
  • Check Cursor’s developer console (Help → Toggle Developer Tools)
OpenCode:
  • Run OpenCode with DEBUG=mcp environment variable

Schema Validation

Zod schemas validate arguments before forwarding to daemon:
{
  name: "set_property",
  schema: {
    path: z.string(),        // Required string
    property: z.string(),    // Required string
    value: z.any()           // Any type
  }
}
If the AI provides invalid arguments (wrong type, missing required field), the MCP SDK automatically returns a validation error before the daemon is called.

Performance Characteristics

Startup Time

~100-200ms — Node.js startup + MCP SDK initialization

Tool Call Latency

50-200ms typical (network + daemon + plugin)
Up to 120s for timeouts

Concurrent Tools

Multiple tool calls execute sequentially in the plugin
MCP server handles concurrent requests from AI

Memory Usage

~20-40 MB for Node.js + MCP SDK

Security Considerations

The MCP server trusts the AI assistant and forwards all tool calls to the daemon without additional authentication. Only use RbxGenie with trusted AI assistants in local development environments.
Security model:
  1. No network exposure — Daemon binds to localhost only
  2. No authentication — Assumes single-user local machine
  3. Full Studio accessexecute_luau can run arbitrary code
  4. AI trust boundary — AI assistant must be trusted
Do not expose the daemon port (7766) to the network. Do not run RbxGenie on shared machines or production environments.

Next Steps

Architecture Overview

Understand the complete system architecture

MCP Integration

Learn how to configure MCP for your AI assistant

Build docs developers (and LLMs) love