Editor/SceneToolDefinitions.cs
using System.Collections.Generic;
namespace SboxMcpServer;
/// <summary>
/// MCP tool schema definitions for scene-inspection tools.
/// </summary>
internal static class SceneToolDefinitions
{
internal static Dictionary<string, object> GetSceneSummary => new()
{
["name"] = "get_scene_summary",
["description"] =
"Returns a high-level overview of the active scene without listing every object. " +
"Includes: total/root/enabled/disabled object counts, all unique tags in use, " +
"a component-type frequency table, a prefab source breakdown (which prefabs have how many instances), " +
"a network mode distribution, and a root object list. " +
"Call this FIRST before any other scene tool to orient yourself. " +
"Also use this when the user asks 'what tags are in use?', 'what types of objects are in the scene?', " +
"'which prefabs are most used?', or 'give me an overview of the scene'.",
["inputSchema"] = new Dictionary<string, object>
{
["type"] = "object",
["properties"] = new Dictionary<string, object>()
}
};
internal static Dictionary<string, object> GetSceneHierarchy => new()
{
["name"] = "get_scene_hierarchy",
["description"] =
"Lists GameObjects in the active scene as an indented tree. " +
"Use rootOnly=true first to get a short top-level overview before expanding. " +
"Use rootId to walk only a specific subtree (e.g. just the Ship or just the Units container) " +
"instead of dumping the entire scene. " +
"Each entry shows name, ID, enabled state, tags, and component types. " +
"AVOID calling this on large scenes without rootOnly=true or rootId — it can return thousands of lines. " +
"For questions like 'are there any X objects?' use find_game_objects instead. " +
"For a quick scene overview use get_scene_summary instead.",
["inputSchema"] = new Dictionary<string, object>
{
["type"] = "object",
["properties"] = new Dictionary<string, object>
{
["rootOnly"] = new Dictionary<string, object>
{
["type"] = "boolean",
["description"] = "If true, only list root-level GameObjects (no children). Default false."
},
["includeDisabled"] = new Dictionary<string, object>
{
["type"] = "boolean",
["description"] = "If false, exclude disabled GameObjects. Default true (include all)."
},
["rootId"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "GUID of a GameObject. If provided, only that object's subtree is listed. Use this to inspect a specific container (e.g. the Ship or Units) without dumping the whole scene."
}
}
}
};
internal static Dictionary<string, object> FindGameObjects => new()
{
["name"] = "find_game_objects",
["description"] =
"Search and filter GameObjects in the active scene by name, tag, component type, path, " +
"network root status, or prefab instance status. Includes disabled objects and those inside " +
"disabled parents (unlike the old version). " +
"Use this whenever the user asks whether something exists in the scene, " +
"e.g. 'are there any crystals?', 'how many NPCs?', 'find all doors', 'is there a SpawnPoint?'. " +
"Also use this to locate an object before calling get_game_object_details. " +
"Filters are ANDed together. Results can be sorted by name, distance, or componentCount. " +
"Returns each match's ID, scene path, tags, component types, world position, child count, " +
"isPrefabInstance, prefabSource, isNetworkRoot, and networkMode. " +
"Never guess whether an object exists — always call this tool to check.",
["inputSchema"] = new Dictionary<string, object>
{
["type"] = "object",
["properties"] = new Dictionary<string, object>
{
["nameContains"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Case-insensitive substring to match against GameObject names."
},
["hasTag"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Only return objects that have this tag."
},
["hasComponent"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Only return objects that have a component whose type name contains this string (case-insensitive). E.g. 'Rigidbody', 'ResourceNode', 'ModelRenderer'."
},
["pathContains"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Only return objects whose full scene path contains this string. E.g. 'Units/' to find everything under the Units container."
},
["enabledOnly"] = new Dictionary<string, object>
{
["type"] = "boolean",
["description"] = "If true, only return enabled GameObjects. Default false (return all, including disabled)."
},
["isNetworkRoot"] = new Dictionary<string, object>
{
["type"] = "boolean",
["description"] = "If set, filter to objects that are (true) or are not (false) a network root."
},
["isPrefabInstance"] = new Dictionary<string, object>
{
["type"] = "boolean",
["description"] = "If set, filter to objects that are (true) or are not (false) prefab instances."
},
["maxResults"] = new Dictionary<string, object>
{
["type"] = "integer",
["description"] = "Maximum number of results to return. Default 50, max 500."
},
["sortBy"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Sort results by: 'name', 'distance' (requires sortOriginX/Y/Z), or 'componentCount'."
},
["sortOriginX"] = new Dictionary<string, object>
{
["type"] = "number",
["description"] = "X coordinate of the origin for distance sorting."
},
["sortOriginY"] = new Dictionary<string, object>
{
["type"] = "number",
["description"] = "Y coordinate of the origin for distance sorting."
},
["sortOriginZ"] = new Dictionary<string, object>
{
["type"] = "number",
["description"] = "Z coordinate of the origin for distance sorting."
}
}
}
};
internal static Dictionary<string, object> FindGameObjectsInRadius => new()
{
["name"] = "find_game_objects_in_radius",
["description"] =
"Find all GameObjects within a given world-space radius of a point, sorted by distance. " +
"Use this for spatial questions: 'what's near the player?', 'which resource nodes are close to my building?', " +
"'what units are within attack range?'. " +
"Optionally filter by tag or component type. Results include distanceFromOrigin. " +
"Get the origin coordinates from a prior get_game_object_details call.",
["inputSchema"] = new Dictionary<string, object>
{
["type"] = "object",
["properties"] = new Dictionary<string, object>
{
["x"] = new Dictionary<string, object>
{
["type"] = "number",
["description"] = "World X coordinate of the search origin."
},
["y"] = new Dictionary<string, object>
{
["type"] = "number",
["description"] = "World Y coordinate of the search origin."
},
["z"] = new Dictionary<string, object>
{
["type"] = "number",
["description"] = "World Z coordinate of the search origin."
},
["radius"] = new Dictionary<string, object>
{
["type"] = "number",
["description"] = "Search radius in world units. Default 1000."
},
["hasTag"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Only return objects with this tag."
},
["hasComponent"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Only return objects with a component whose type name contains this string."
},
["enabledOnly"] = new Dictionary<string, object>
{
["type"] = "boolean",
["description"] = "If true, only return enabled GameObjects. Default false."
},
["maxResults"] = new Dictionary<string, object>
{
["type"] = "integer",
["description"] = "Maximum number of results. Default 50, max 500."
}
},
["required"] = new[] { "x", "y", "z" }
}
};
internal static Dictionary<string, object> GetGameObjectDetails => new()
{
["name"] = "get_game_object_details",
["description"] =
"Get full details for one specific GameObject by its GUID or exact name. " +
"Use this when the user asks about a specific object's position, rotation, scale, " +
"what components it has, whether it is enabled, who its parent is, or what its children are. " +
"Prefer using the 'id' (GUID) from a prior find_game_objects call rather than guessing by name. " +
"Set includeChildrenRecursive=true to get the full subtree in one call (useful for ships, buildings, etc.). " +
"Returns world and local transform, all components with enabled state, tags, parent ref, children list, " +
"network mode, prefab source, and isNetworkRoot.",
["inputSchema"] = new Dictionary<string, object>
{
["type"] = "object",
["properties"] = new Dictionary<string, object>
{
["id"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "The GUID of the GameObject (preferred)."
},
["name"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Exact name of the GameObject. If multiple objects share the name, the first match is returned."
},
["includeChildrenRecursive"] = new Dictionary<string, object>
{
["type"] = "boolean",
["description"] = "If true, children are returned as full detail objects recursively instead of a shallow summary. Useful for inspecting a whole prefab tree. Default false."
}
}
}
};
internal static Dictionary<string, object> GetComponentProperties => new()
{
["name"] = "get_component_properties",
["description"] =
"Get the runtime property values of a specific component on a GameObject. " +
"Use this when you need to know the actual data inside a component — not just that it exists. " +
"Examples: 'what is the UnitHealth of this drone?', 'what resource type does this ResourceNode hold?', " +
"'what are the PlayerResources values?', 'what is the Ship's current state?'. " +
"Returns all readable public properties with their current values. " +
"Use find_game_objects or get_game_object_details first to get the object's id. " +
"You MUST provide either 'id' or 'name' in addition to 'componentType'.",
["inputSchema"] = new Dictionary<string, object>
{
["type"] = "object",
["properties"] = new Dictionary<string, object>
{
["id"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "The GUID of the GameObject (preferred). Either 'id' or 'name' must be provided."
},
["name"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Exact name of the GameObject. Either 'id' or 'name' must be provided."
},
["componentType"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Case-insensitive substring of the component type name. E.g. 'UnitHealth', 'ResourceNode', 'Ship', 'PlayerResources'."
}
},
["required"] = new[] { "componentType" }
}
};
internal static Dictionary<string, object> GetPrefabInstances => new()
{
["name"] = "get_prefab_instances",
["description"] =
"Find all instances of a specific prefab in the scene, or get a breakdown of all prefabs and their instance counts. " +
"Use this when the user asks 'how many drones do I have?', 'are all my buildings using the same prefab?', " +
"'what prefabs are in the scene?', or 'find all instances of constructor_drone.prefab'. " +
"If prefabPath is omitted, returns a full breakdown of all prefab sources and counts. " +
"prefabPath is matched as a case-insensitive substring of the full prefab path.",
["inputSchema"] = new Dictionary<string, object>
{
["type"] = "object",
["properties"] = new Dictionary<string, object>
{
["prefabPath"] = new Dictionary<string, object>
{
["type"] = "string",
["description"] = "Substring of the prefab path to match. E.g. 'constructor_drone', 'astro_bus', 'starter_building'. Omit to get a full breakdown of all prefabs."
},
["enabledOnly"] = new Dictionary<string, object>
{
["type"] = "boolean",
["description"] = "If true, only count/return enabled instances. Default false."
},
["maxResults"] = new Dictionary<string, object>
{
["type"] = "integer",
["description"] = "Maximum number of instance results to return. Default 100, max 500."
}
}
}
};
}