Esta página foi traduzida automaticamente do inglês. Leia o original em inglês.

Ferramentas personalizadas

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.

Atualizado em 4 de novembro de 2026 Ver como Markdown

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.

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.

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:

PropriedadeSignificado
ReadOnlyHintA ferramenta apenas lê dados.
DestructiveHintA ferramenta pode excluir ou sobrescrever dados.
IdempotentHintChamá-la novamente com os mesmos argumentos não muda mais nada.
OpenWorldHintA ferramenta se comunica com sistemas externos, como a web.
Visiblefalse oculta a ferramenta da lista do agente quando descoberta pela primeira vez; ela ainda é executada quando chamada pelo nome.
TargetToolTarget.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]:

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:

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:

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.

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).

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*.