# Ferramentas personalizadas — Unity · AI Game Developer Wiki

Crie ferramentas, prompts, recursos e skills do Unity MCP em C# com os atributos AiTool, rode na thread principal e use em um jogo compilado.

Esta página foi traduzida automaticamente do inglês. [Leia o original em inglês](https://ai-game.dev/docs/wiki/unity/custom-tools).

O Unity MCP pode transformar qualquer método C# do seu projeto em uma ferramenta que o agente de IA pode chamar. É assim que você dá ao agente habilidades específicas do projeto: "gerar uma onda de inimigos", "reconstruir o nível a partir deste JSON", "validar todos os prefabs de itens". Esta página mostra a API de atributos para ferramentas, prompts, recursos e skills, como fazer chamadas à API do Unity com segurança e como expor ferramentas de um jogo em execução.

## Antes de escrever uma ferramenta

O RQL já lê e define membros dos seus próprios componentes e chama seus métodos públicos, então muitas tarefas não precisam de nenhuma ferramenta personalizada. Escreva uma ferramenta quando quiser uma operação única e bem descrita, com validação, ou uma lógica que não deva ser improvisada pelo modelo a cada vez. Para fazer o RQL entender melhor os seus próprios tipos, veja [adaptadores RQL personalizados](https://ai-game.dev/docs/rql/custom-adapters).

## Sua primeira ferramenta

Uma ferramenta é um método marcado com `[AiTool]` dentro de uma classe marcada com `[AiToolType]`. Ambos os atributos estão no namespace `com.IvanMurzak.McpPlugin`.

```csharp
using System.ComponentModel;
using com.IvanMurzak.McpPlugin;
using com.IvanMurzak.ReflectorNet.Utils;
using UnityEngine;

[AiToolType]
public static class Tool_Enemies
{
    [AiTool("enemies-spawn-wave", Title = "Enemies / Spawn wave")]
    [Description("Spawns a wave of enemy prefabs in a circle around the origin. Returns how many were spawned.")]
    public static string SpawnWave
    (
        [Description("Asset path of the enemy prefab, e.g. 'Assets/Prefabs/Enemy.prefab'.")]
        string prefabPath,
        [Description("How many enemies to spawn.")]
        int count = 5,
        [Description("Circle radius in meters.")]
        float radius = 10f
    )
    {
        return MainThread.Instance.Run(() =>
        {
            var prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
            if (prefab == null)
                return $"[Error] No prefab at '{prefabPath}'.";

            for (var i = 0; i < count; i++)
            {
                var angle = i * Mathf.PI * 2f / count;
                var position = new Vector3(Mathf.Cos(angle), 0f, Mathf.Sin(angle)) * radius;
                Object.Instantiate(prefab, position, Quaternion.identity);
            }
            return $"[Success] Spawned {count} enemies.";
        });
    }
}
```

Coloque código exclusivo do Editor como este (ele usa `UnityEditor`) em uma pasta `Editor` ou em uma assembly de Editor. O plugin escaneia as assemblies do projeto quando inicia e após cada recompilação, então a ferramenta aparece na lista do agente sem nenhum registro. Reconecte o agente se ele não atualizar a lista de ferramentas sozinho.

### Nomes e descrições

- O primeiro argumento de `[AiTool]` é o id da ferramenta que o agente chama. Use kebab-case, como nas ferramentas integradas (`rql-query`, `tests-run`).
- `Title` é um nome legível por humanos exibido nas listas de ferramentas.
- `[Description]` (de `System.ComponentModel`) no método e em cada parâmetro é o que o modelo lê. Diga o que a ferramenta faz, quando chamá-la e o que cada valor significa, com um exemplo.
- Parâmetros com valor padrão, ou anuláveis como `string? tag = null`, são opcionais para o modelo.

### Valores de retorno

Uma ferramenta pode retornar uma `string`, qualquer objeto serializável (o plugin o converte em JSON e descreve seu esquema ao modelo), `Task<T>` para trabalho assíncrono, ou um `ResponseCallTool` de `com.IvanMurzak.McpPlugin.Common.Model` quando você quiser controle explícito, por exemplo `ResponseCallTool.Success("…")`, `ResponseCallTool.Error("…")` ou `ResponseCallTool.Image(bytes, "image/png")`. Retorne mensagens claras de sucesso e erro: o modelo as usa para decidir o que fazer em seguida.

## A thread principal

As ferramentas podem ser invocadas fora da thread principal do Unity, e a maioria das APIs do Unity lança exceção quando chamada de outra thread. Envolva as chamadas ao Unity em `MainThread.Instance.Run(...)` (namespace `com.IvanMurzak.ReflectorNet.Utils`), ou `MainThread.Instance.RunAsync(...)` em uma ferramenta assíncrona. Mantenha o trabalho pesado que não toca no Unity, como analisar um arquivo grande, fora do wrapper para que o Editor continue responsivo.

## Opções da ferramenta

`[AiTool]` aceita dicas opcionais que informam aos clientes MCP como a ferramenta se comporta:

| Propriedade | Significado |
| --- | --- |
| `ReadOnlyHint` | A ferramenta apenas lê dados. |
| `DestructiveHint` | A ferramenta pode excluir ou sobrescrever dados. |
| `IdempotentHint` | Chamá-la novamente com os mesmos argumentos não muda mais nada. |
| `OpenWorldHint` | A ferramenta se comunica com sistemas externos, como a web. |
| `Visible` | `false` oculta a ferramenta da lista do agente quando descoberta pela primeira vez; ela ainda é executada quando chamada pelo nome. |
| `Target` | `ToolTarget.Editor`, `ToolTarget.Runtime` ou `ToolTarget.Both` (padrão). Também pode ser definido em `[AiToolType]`. |

Exemplo: `[AiTool("level-validate", Title = "Level / Validate", ReadOnlyHint = true, Visible = false)]`.

## Prompts personalizados

Um prompt é uma instrução reutilizável que o usuário pode invocar a partir do cliente de IA, muitas vezes como um comando de barra. Marque uma classe com `[AiPromptType]` e um método que retorna string com `[AiPrompt]`:

```csharp
using System.ComponentModel;
using com.IvanMurzak.McpPlugin;
using com.IvanMurzak.McpPlugin.Common.Model;

[AiPromptType]
public static class Prompt_ProjectRules
{
    [AiPrompt(Name = "project-conventions", Role = Role.User)]
    [Description("Coding and scene conventions for this project.")]
    public static string Conventions()
        => "Use PascalCase for public members. Put new UI under the MainCanvas object. Prefer object pooling for bullets.";
}
```

`Role` é `Role.User` ou `Role.Assistant`. Defina `Enabled = false` para entregar um prompt oculto por padrão; os prompts integrados fazem isso.

## Recursos personalizados

Um recurso são dados somente leitura que o cliente pode buscar por URI. Marque uma classe com `[AiResourceType]` e um método com `[AiResource]`, e forneça um método de listagem:

```csharp
using System.ComponentModel;
using com.IvanMurzak.McpPlugin;
using com.IvanMurzak.McpPlugin.Common.Model;

[AiResourceType]
public static class Resource_Balance
{
    const string Uri = "game://balance";

    [AiResource(Route = Uri, Name = "balance", MimeType = "application/json", ListResources = nameof(List))]
    [Description("Current game balance values as JSON.")]
    public static ResponseResourceContent[] Get(string uri)
        => new[] { ResponseResourceContent.CreateText(uri, "{\"enemyHp\":100}", "application/json") };

    public static ResponseListResource[] List()
        => new[] { new ResponseListResource(uri: Uri, name: "balance", mimeType: "application/json", description: "Game balance") };
}
```

## Skills personalizadas

Skills são arquivos Markdown gerados na pasta de skills do agente (por exemplo, `.claude/skills`). Adicione as suas com `[AiSkillType]` em uma classe estática e `[AiSkill]` em uma propriedade ou campo estático de string que contém o Markdown:

```csharp
using com.IvanMurzak.McpPlugin;

[AiSkillType]
public static class Skill_MyGame
{
    [AiSkill("my-game-levels", "How levels are structured in this project and which tools build them.")]
    public static string Markdown => "# Levels\n\nEvery level is a scene under Assets/Levels ...";
}
```

Em um método de ferramenta, `[AiSkillDescription]` e `[AiSkillBody]` adicionam um resumo curto e um Markdown longo à skill gerada para essa ferramenta. Regenere as skills com **Skills > Generate** na janela ou `unity-mcp-cli setup-skills <agent>`.

## Ferramentas em um jogo em execução

Os mesmos atributos funcionam em uma build de player. Em um jogo, nada se conecta por padrão: você inicializa o plugin de runtime por conta própria e registra as ferramentas que quiser. Marque as ferramentas exclusivas do jogo com `Target = ToolTarget.Runtime` (namespace `com.IvanMurzak.McpPlugin.Common`). O Editor então as ignora, e o servidor encaminha suas chamadas ao jogo em execução mesmo enquanto o Editor está conectado.

```csharp
using System.ComponentModel;
using System.Reflection;
using System.Threading.Tasks;
using com.IvanMurzak.McpPlugin;
using com.IvanMurzak.McpPlugin.Common;
using com.IvanMurzak.ReflectorNet.Utils;
using com.IvanMurzak.Unity.MCP;
using UnityEngine;

[AiToolType(Target = ToolTarget.Runtime)]
public static class ChessGameAI
{
    [AiTool("chess-do-turn", Title = "Do the turn")]
    [Description("Moves a figure. Returns true if the move was accepted.")]
    public static Task<bool> DoTurn(int figureId, Vector2Int position)
        => MainThread.Instance.RunAsync(() => ChessGameController.Instance.DoTurn(figureId, position));

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void SetupMcp()
    {
        var mcpPlugin = UnityMcpPluginRuntime.Initialize(builder =>
            {
                builder.WithConfig(c => c.Host = "http://localhost:8080");
                builder.WithToolsFromAssembly(Assembly.GetExecutingAssembly());
            })
            .Build();
        _ = mcpPlugin.Connect();
    }
}
```

`ChessGameController` representa o código do seu próprio jogo. Para autenticar em um servidor, defina `c.CredentialProvider` como uma função que retorna o token. Chame `UnityMcpPluginRuntime.DisposeInstance()` para encerrar a conexão de runtime. Os jogadores precisam de um servidor MCP alcançável, então planeje onde ele roda (veja [self-hosting](https://ai-game.dev/pt-br/docs/wiki/unity/configuration#self-hosting)).

## Solução de problemas com ferramentas personalizadas

- **A ferramenta não aparece.** Verifique o Console em busca de erros de compilação, confirme que ambos os atributos estão presentes e abra **Tools > Open** na janela: a ferramenta pode estar oculta. Assemblies cujos nomes começam com `Unity.`, `UnityEngine`, `UnityEditor`, `System` ou `Microsoft` não são escaneadas, então dê outro nome à sua assembly.
- **Erros de "Can only be called from the main thread".** Envolva as chamadas ao Unity em `MainThread.Instance.Run`.
- **O modelo chama a ferramenta de forma errada.** Melhore os textos de `[Description]` e adicione exemplos de valores válidos.

Os nomes antigos `[McpPluginToolType]` / `[McpPluginTool]` ainda compilam, mas estão obsoletos; use os atributos `Ai*`.

## Páginas relacionadas

- [Ferramentas e RQL](https://ai-game.dev/pt-br/docs/wiki/unity/tools-and-rql) para as ferramentas integradas.
- [Adaptadores RQL personalizados](https://ai-game.dev/docs/rql/custom-adapters).
- [Configuração](https://ai-game.dev/pt-br/docs/wiki/unity/configuration#tools-prompts-resources) para mostrar ou ocultar ferramentas.

---

Source (HTML): https://ai-game.dev/pt-br/docs/wiki/unity/custom-tools

Machine-translated (pt-BR) from the English page: https://ai-game.dev/docs/wiki/unity/custom-tools
