# Benutzerdefinierte Tools — Unity · AI Game Developer Wiki

Eigene Unity MCP Tools, Prompts, Ressourcen und Skills in C# mit AiTool-Attributen schreiben, im Main Thread ausführen und im fertigen Spiel nutzen.

Diese Seite wurde maschinell aus dem Englischen übersetzt. [Englisches Original lesen](https://ai-game.dev/docs/wiki/unity/custom-tools).

Unity MCP kann jede C#-Methode in deinem Projekt in ein Tool verwandeln, das der AI-Agent aufrufen kann. So gibst du dem Agenten projektspezifische Fähigkeiten: „eine Gegnerwelle spawnen“, „das Level aus diesem JSON neu aufbauen“, „alle Item-Prefabs validieren“. Diese Seite zeigt die Attribut-API für Tools, Prompts, Ressourcen und Skills, wie du Unity-API-Aufrufe sicher ausführst und wie du Tools aus einem laufenden Spiel bereitstellst.

## Bevor du ein Tool schreibst

RQL liest und setzt bereits Members deiner eigenen Komponenten und ruft deren öffentliche Methoden auf, daher brauchen viele Aufgaben gar kein eigenes Tool. Schreibe ein Tool, wenn du eine einzelne, gut beschriebene Operation mit Validierung willst oder Logik, die das Modell nicht jedes Mal improvisieren soll. Wenn RQL stattdessen deine eigenen Typen besser verstehen soll, siehe [benutzerdefinierte RQL-Adapter](https://ai-game.dev/docs/rql/custom-adapters).

## Dein erstes Tool

Ein Tool ist eine Methode, die mit `[AiTool]` markiert ist, innerhalb einer Klasse, die mit `[AiToolType]` markiert ist. Beide Attribute befinden sich im 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.";
        });
    }
}
```

Lege Editor-only-Code wie diesen (er verwendet `UnityEditor`) in einem `Editor`-Ordner oder einer Editor-Assembly ab. Das Plugin durchsucht die Assemblies des Projekts beim Start und nach jeder Neukompilierung, sodass das Tool ohne jegliche Registrierung in der Liste des Agenten erscheint. Verbinde den Agenten neu, wenn er seine Tool-Liste nicht selbst aktualisiert.

### Benennung und Beschreibungen

- Das erste Argument von `[AiTool]` ist die Tool-ID, die der Agent aufruft. Verwende Kebab-Case wie bei den eingebauten Tools (`rql-query`, `tests-run`).
- `Title` ist ein menschenlesbarer Name, der in Tool-Listen angezeigt wird.
- `[Description]` (aus `System.ComponentModel`) an der Methode und an jedem Parameter ist das, was das Modell liest. Sag, was das Tool tut, wann man es aufruft und was jeder Wert bedeutet – mit einem Beispiel.
- Parameter mit einem Standardwert oder nullable Parameter wie `string? tag = null` sind für das Modell optional.

### Rückgabewerte

Ein Tool kann einen `string`, ein beliebiges serialisierbares Objekt (das Plugin wandelt es in JSON um und beschreibt dem Modell sein Schema), `Task<T>` für asynchrone Arbeit oder ein `ResponseCallTool` aus `com.IvanMurzak.McpPlugin.Common.Model` zurückgeben, wenn du explizite Kontrolle willst, zum Beispiel `ResponseCallTool.Success("…")`, `ResponseCallTool.Error("…")` oder `ResponseCallTool.Image(bytes, "image/png")`. Gib klare Erfolgs- und Fehlermeldungen zurück: Das Modell nutzt sie, um zu entscheiden, was als Nächstes zu tun ist.

## Der Main Thread

Tools können außerhalb des Main Threads von Unity aufgerufen werden, und die meisten Unity-APIs werfen eine Exception, wenn sie aus einem anderen Thread aufgerufen werden. Umhülle Unity-Aufrufe mit `MainThread.Instance.Run(...)` (Namespace `com.IvanMurzak.ReflectorNet.Utils`) oder `MainThread.Instance.RunAsync(...)` aus einem asynchronen Tool. Halte schwere Arbeit, die Unity nicht berührt, etwa das Parsen einer großen Datei, außerhalb des Wrappers, damit der Editor responsiv bleibt.

## Tool-Optionen

`[AiTool]` akzeptiert optionale Hinweise, die MCP-Clients mitteilen, wie sich das Tool verhält:

| Eigenschaft | Bedeutung |
| --- | --- |
| `ReadOnlyHint` | Das Tool liest nur Daten. |
| `DestructiveHint` | Das Tool kann Daten löschen oder überschreiben. |
| `IdempotentHint` | Ein erneuter Aufruf mit denselben Argumenten ändert nichts weiter. |
| `OpenWorldHint` | Das Tool kommuniziert mit externen Systemen, etwa dem Web. |
| `Visible` | `false` verbirgt das Tool bei der ersten Erkennung aus der Liste des Agenten; es läuft weiterhin, wenn es per Namen aufgerufen wird. |
| `Target` | `ToolTarget.Editor`, `ToolTarget.Runtime` oder `ToolTarget.Both` (Standard). Kann auch an `[AiToolType]` gesetzt werden. |

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

## Benutzerdefinierte Prompts

Ein Prompt ist eine wiederverwendbare Anweisung, die der Benutzer im AI-Client aufrufen kann, oft als Slash-Befehl. Markiere eine Klasse mit `[AiPromptType]` und eine Methode, die einen String zurückgibt, mit `[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` ist `Role.User` oder `Role.Assistant`. Setze `Enabled = false`, um einen Prompt standardmäßig verborgen auszuliefern; die eingebauten Prompts machen das.

## Benutzerdefinierte Ressourcen

Eine Ressource sind schreibgeschützte Daten, die der Client per URI abrufen kann. Markiere eine Klasse mit `[AiResourceType]` und eine Methode mit `[AiResource]` und stelle eine Auflistungsmethode bereit:

```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") };
}
```

## Benutzerdefinierte Skills

Skills sind Markdown-Dateien, die in den Skills-Ordner des Agenten generiert werden (zum Beispiel `.claude/skills`). Füge eigene hinzu mit `[AiSkillType]` an einer statischen Klasse und `[AiSkill]` an einer statischen String-Eigenschaft oder einem Feld, das das Markdown enthält:

```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 ...";
}
```

An einer Tool-Methode fügen `[AiSkillDescription]` und `[AiSkillBody]` eine kurze Zusammenfassung und ausführliches Markdown zum generierten Skill für dieses Tool hinzu. Generiere die Skills mit **Skills > Generate** im Fenster oder mit `unity-mcp-cli setup-skills <agent>` neu.

## Tools in einem laufenden Spiel

Dieselben Attribute funktionieren in einem Player-Build. In einem Spiel verbindet sich standardmäßig nichts: Du initialisierst das Runtime-Plugin selbst und registrierst die gewünschten Tools. Markiere reine Spiel-Tools mit `Target = ToolTarget.Runtime` (Namespace `com.IvanMurzak.McpPlugin.Common`). Der Editor überspringt sie dann, und der Server leitet ihre Aufrufe an das laufende Spiel weiter, auch während der Editor verbunden ist.

```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` steht für deinen eigenen Spielcode. Um dich gegenüber einem Server zu authentifizieren, setze `c.CredentialProvider` auf eine Funktion, die das Token zurückgibt. Rufe `UnityMcpPluginRuntime.DisposeInstance()` auf, um die Runtime-Verbindung zu beenden. Spieler brauchen einen erreichbaren MCP-Server, plane also, wo er läuft (siehe [Self-Hosting](https://ai-game.dev/de/docs/wiki/unity/configuration#self-hosting)).

## Fehlerbehebung bei benutzerdefinierten Tools

- **Das Tool erscheint nicht.** Prüfe die Console auf Kompilierungsfehler, bestätige, dass beide Attribute vorhanden sind, und öffne **Tools > Open** im Fenster: Möglicherweise ist das Tool verborgen. Assemblies, deren Namen mit `Unity.`, `UnityEngine`, `UnityEditor`, `System` oder `Microsoft` beginnen, werden nicht durchsucht, gib deiner Assembly daher einen anderen Namen.
- **Fehler „Can only be called from the main thread“.** Umhülle die Unity-Aufrufe mit `MainThread.Instance.Run`.
- **Das Modell ruft das Tool falsch auf.** Verbessere die `[Description]`-Texte und füge Beispiele für gültige Werte hinzu.

Die älteren Namen `[McpPluginToolType]` / `[McpPluginTool]` kompilieren noch, sind aber veraltet; verwende die `Ai*`-Attribute.

## Verwandte Seiten

- [Tools und RQL](https://ai-game.dev/de/docs/wiki/unity/tools-and-rql) für die eingebauten Tools.
- [Benutzerdefinierte RQL-Adapter](https://ai-game.dev/docs/rql/custom-adapters).
- [Konfiguration](https://ai-game.dev/de/docs/wiki/unity/configuration#tools-prompts-resources) zum Ein- oder Ausblenden von Tools.

---

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

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