# Adding MCP tools to a project
A project or addon can give agents its own tools. A tool is a public static method with
`[McpTool]`. There is no registration: the registry finds tools through the type library, so a
tool appears as soon as the code compiles and hotloads, and `search_tools` / `call_tool` reach it
without reconnecting.
Put editor-only tools in `Editor/`; tools that must also work in a running game (`--game`, or a
game started with `-mcp`) go in `Code/`.
```csharp
namespace Editor.Mcp;
[McpToolset( "car", "Everything about the cars in the scene" )]
public static class CarTools
{
///
/// Find cars by name. Returns each car's id - pass one to get_car for details.
///
/// Case insensitive substring of the car's name. Empty matches everything.
/// How many results to return. Default 20, max 100.
[McpTool( "find_cars" )]
public static object FindCars( string query = "", int limit = 20 )
{
...
return new { Total = total, Cars = cars };
}
}
```
- `[McpTool( "name" )]` marks the method. Without a name it's derived as snake_case
(`FindCars` → `find_cars`). Names are API — renaming one breaks agent workflows.
`[McpTool.ReadOnly( ... )]` marks tools that don't change anything.
- `[McpToolset( name, description )]` groups the class's tools for `list_toolsets`. Without it the
name is derived from the class (`CarTools` → `car`).
- In project/addon code, the XML `` becomes the tool description and `` docs the
parameter descriptions. **The description is the interface**: say what it returns, what to call
next, and anything surprising. Refer to other tools by name ("id as returned by find_cars").
## Parameters
A parameter with a default is optional; without one it's required.
| C# type | Agent sends |
|---|---|
| `string` | A string (anything else is stringified — the most forgiving type) |
| `bool` | `true` / `false` |
| integer and floating types | A number |
| enums | The name as a string, case insensitive; the schema lists the values |
| `Vector2/3/4`, `Vector2Int/3Int`, `Angles`, `Rotation` | A comma string: `"100,0,50"` |
| `Guid`, `DateTime`, `TimeSpan`, `char` | A string |
| `T[]`, `List` | An array |
| `Dictionary` | An object |
| `JsonObject`, `JsonArray`, `JsonNode` | Raw JSON |
| Your own class or struct | An object built from its public settable properties (camelCase in the schema, case-insensitive binding, C# `required` enforced, `[JsonPropertyName]` / `[JsonIgnore]` respected) |
Binding is tolerant of common mistakes — case, JSON-encoded strings, numbers as strings — but
unknown argument names fail loudly. Engine types other than those listed keep an open schema;
prefer the listed types.
## Return values
| Return | Agent gets |
|---|---|
| `string` | The text |
| any object | JSON (anonymous objects are the usual shape) |
| `GameObject` / `Component` at any depth | A compact reference with its id, never a serialized subtree |
| `Resource` | Its resource path |
| `Bitmap` | A PNG image |
| `McpResult` | Full control, e.g. `McpResult.Image( thumb ).WithText( path )` |
| `null` / `void` | `ok` |
| `Task` | Awaited, then shaped as above |
Return counts alongside truncated lists (`new { Total, Showing, Items }`), format dates as
strings and leave out fields nobody asked for. Declaring a named data class as the return type
(instead of `object`) also advertises an `outputSchema` and fills `structuredContent`.
## Errors, threading
- **Throw** to report failure. The message reaches the agent as an `isError` result, so write it
for that reader: what was wrong and what to do instead.
`throw new Exception( $"No car named '{name}'. Find cars with find_cars." );`
- Tools run on the main thread; touch scenes, assets and widgets freely. `async` tools are awaited
off the main thread after the first `await`, so do slow work after your last engine access.
- If the main thread doesn't pick a call up within 30 seconds the agent is told the editor is
blocked, and the call is cancelled.
## Conventions to follow
- Paging is `limit` and `offset`; state the default and max in the parameter description.
- Vectors and angles are comma strings. Coordinates are Source convention (inches, +X forward,
+Y left, +Z up, degrees).
- Identify game objects and components by GUID, assets by the relative path `asset_search` returns.
- Document undo behaviour for anything that edits a scene.
After saving, check `editor_status` for a clean compile, then `search_tools` for your tool's name.