How to Create Custom Command Handlers in Unity MCP: A Complete Guide
You can create custom command handlers in Unity MCP by extending BaseCommandHandler in TypeScript for the server side and implementing IMcpCommandHandler in C# for the Unity Editor side, then placing them in the respective handlers folders for automatic discovery.
The Unity MCP project by isuzu-shiranui/unitymcp bridges the gap between AI assistants and the Unity Editor through a Model Context Protocol (MCP) implementation. Creating custom command handlers allows you to expose new Unity Editor functionality to external AI tools, enabling automated scene manipulation, asset management, and workflow optimization.
Understanding the Unity MCP Command Architecture
Unity MCP operates as a bidirectional bridge with two distinct components that handle command processing.
The TypeScript Server Side (unity-mcp-ts)
The MCP server, located in unity-mcp-ts/, exposes a JSON-RPC-like API that receives requests from AI clients. When a request arrives containing a command string like "menu.execute", the server:
- Parses the command to extract the prefix (
"menu") and action ("execute") - Looks up a registered handler implementing
ICommandHandlerwith a matchingcommandPrefix - Invokes the handler's
executemethod, which typically forwards the request to Unity viasendUnityRequest
The HandlerDiscovery class in unity-mcp-ts/src/core/HandlerDiscovery.ts automates registration by scanning the src/handlers/ directory, instantiating each handler, injecting the shared UnityConnection, and registering it with CommandRegistry【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/unity-mcp-ts/src/core/HandlerDiscovery.ts#L80-L112】.
The C# Unity Editor Side
For commands requiring Unity Editor interaction, you need a C# counterpart in jp.shiranui-isuzu.unity-mcp/Editor/. These handlers implement IMcpCommandHandler and receive requests forwarded from the TypeScript server.
The McpEditorInitializer in Editor/Core/McpEditorInitializer.cs uses McpHandlerDiscovery<IMcpCommandHandler> to automatically discover and register C# handlers at startup【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpEditorInitializer.cs#L34-L38】.
Creating a TypeScript Command Handler
To create a server-side command handler, extend BaseCommandHandler from unity-mcp-ts/src/core/BaseCommandHandler.ts.
Extending BaseCommandHandler
Create a new TypeScript file in unity-mcp-ts/src/handlers/. The BaseCommandHandler class provides essential infrastructure including ensureUnityConnection() to verify Unity availability and sendUnityRequest(command, params) to forward requests to the Unity Editor【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/unity-mcp-ts/src/core/BaseCommandHandler.ts#L40-L104】.
Implementing Required Methods
You must implement three abstract members:
commandPrefix: The string identifier for your command namespace (e.g.,"mycmd"for requests like"mycmd.action")description: Human-readable description of what the handler doesexecuteCommand(action, parameters): The core logic that processes the action and returns aJObject
Optional Tool Definitions
Override getToolDefinitions() to expose parameter schemas for UI tools. This allows AI assistants to understand what parameters your commands accept.
Example: Ping Command Handler
// src/handlers/PingCommandHandler.ts
import { JObject } from "../types/index.js";
import { BaseCommandHandler } from "../core/BaseCommandHandler.js";
import { IMcpToolDefinition } from "../core/interfaces/ICommandHandler.js";
/**
* Simple "ping" command – replies with the server timestamp.
*/
export class PingCommandHandler extends BaseCommandHandler {
public get commandPrefix(): string {
return "ping";
}
public get description(): string {
return "Returns a timestamp confirming the server is alive";
}
public getToolDefinitions(): Map<string, IMcpToolDefinition> {
const tools = new Map<string, IMcpToolDefinition>();
tools.set("ping_now", {
description: "Get the current server timestamp",
parameterSchema: {},
annotations: {
title: "Ping",
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
});
return tools;
}
protected async executeCommand(_: string, __: JObject): Promise<JObject> {
// No need to talk to Unity – just return a JSON payload
return {
success: true,
timestamp: new Date().toISOString(),
};
}
}
This handler responds to "ping.execute" or "ping.ping_now" without contacting Unity, demonstrating how to create standalone server-side commands.
Creating the C# Counterpart Handler
When your command requires Unity Editor functionality, implement IMcpCommandHandler in C#.
Implementing IMcpCommandHandler
Create a new class in jp.shiranui-isuzu.unity-mcp/Editor/Handlers/:
// Editor/Handlers/HelloWorldCommandHandler.cs
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using UnityMCP.Editor.Core;
namespace UnityMCP.Editor.Handlers
{
internal sealed class HelloWorldCommandHandler : IMcpCommandHandler
{
public string CommandPrefix => "hello";
public string Description => "Logs a hello-world message in the Unity console";
public JObject Execute(string action, JObject parameters)
{
if (action.ToLower() != "say")
return new JObject
{
["success"] = false,
["error"] = $"Unsupported action '{action}'. Expected 'say'."
};
// Perform Unity-side work
Debug.Log("[MCP] Hello, world!");
return new JObject { ["success"] = true };
}
}
}
The C# handler receives requests forwarded by the TypeScript sendUnityRequest method. It validates the action, performs Unity Editor operations, and returns JSON results.
Automatic Discovery and Registration
Unity MCP eliminates manual registration through automatic discovery mechanisms on both sides.
On the TypeScript server, HandlerDiscovery scans src/handlers/ at startup, imports each module, instantiates handler classes, injects the UnityConnection dependency, and registers them with CommandRegistry. This happens in the initializeHandlers() method【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/unity-mcp-ts/src/core/HandlerDiscovery.ts#L80-L112】.
On the Unity Editor side, McpEditorInitializer creates a McpHandlerDiscovery<IMcpCommandHandler> instance and calls DiscoverAndRegister() to find all C# implementations of IMcpCommandHandler in the Editor/Handlers/ directory【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpEditorInitializer.cs#L34-L38】.
Simply placing your files in these directories ensures registration on the next server restart or Unity Editor reload.
Testing Your Custom Command
Once implemented, test your handlers using JSON-RPC requests.
For the TypeScript-only ping handler:
{
"command": "ping.execute",
"params": {}
}
Expected response:
{
"success": true,
"timestamp": "2026-03-04T12:34:56.789Z"
}
For the C# hello handler:
{
"command": "hello.say",
"params": {}
}
This logs "[MCP] Hello, world!" in the Unity Console and returns:
{ "success": true }
Summary
-
Extend
BaseCommandHandlerin TypeScript to create server-side command logic inunity-mcp-ts/src/handlers/. -
Implement
IMcpCommandHandlerin C# for Unity Editor-side operations inEditor/Handlers/. -
Use
sendUnityRequestfrom TypeScript to forward requests to the C# implementation when Unity interaction is required. -
Leverage automatic discovery via
HandlerDiscovery(TypeScript) andMcpHandlerDiscovery(C#) — no manual registration required. -
Define tool schemas by overriding
getToolDefinitions()to enable AI assistant parameter discovery.
Frequently Asked Questions
What is the difference between BaseCommandHandler and IMcpCommandHandler?
BaseCommandHandler is the TypeScript abstract class used in the MCP server (unity-mcp-ts) to process incoming JSON-RPC requests and optionally forward them to Unity. IMcpCommandHandler is the C# interface implemented in the Unity Editor to receive those forwarded requests and execute Unity-specific operations. You use BaseCommandHandler for server-side logic and IMcpCommandHandler for Unity-side logic.
Do I need to register custom handlers manually in Unity MCP?
No. Unity MCP uses automatic discovery mechanisms on both sides. On the TypeScript server, HandlerDiscovery scans the src/handlers/ directory and registers any class extending BaseCommandHandler. On the Unity side, McpHandlerDiscovery scans Editor/Handlers/ for implementations of IMcpCommandHandler. Simply place your files in these directories and restart the server or reload the Unity Editor.
Can I create a command handler that doesn't communicate with Unity?
Yes. If your command only requires server-side processing (such as returning status information or performing calculations), you can extend BaseCommandHandler and implement executeCommand without calling sendUnityRequest or ensureUnityConnection. The PingCommandHandler example demonstrates this pattern by returning a timestamp directly from the TypeScript server without contacting the Unity Editor.
Where should I place my custom handler files?
Place TypeScript handlers in unity-mcp-ts/src/handlers/ and ensure they export a class extending BaseCommandHandler. Place C# handlers in jp.shiranui-isuzu.unity-mcp/Editor/Handlers/ (or any Editor/Handlers/ path in your Unity project) and ensure the class implements IMcpCommandHandler. Both locations are scanned automatically by their respective discovery systems.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →