MCP サーバ
コンパイラ本体を AI クライアントから呼べるようにするサーバです。パッケージに同梱されていて、クライアントが設定に従って起動します。
セットアップウィンドウ
Section titled “セットアップウィンドウ”Unity のメニューの TsukimiCode から Tsukimi MCP を開きます。節は 5 つで、上から順に進める構成です。
| 節 | ボタン | 役割 |
|---|---|---|
| Server | なし | 同梱サーバの起動コマンドと、サーバ DLL の存在確認 |
| Client Configuration | Configure | 使うクライアントの設定ファイルへの書き込み |
| Grounding | Install Skill・Update AGENTS.md | AI が参照する手順書のインストール(Skills) |
| Final Check | Install Check・Remove | 作業完了の報告時にコンパイルを検査するフックの設置 |
| How to set this up | なし | ここまでの手順の英語版 |

Server
Section titled “Server”| Start command | クライアントが実際に実行するコマンドがそのまま出ます |
| Status が available | 同梱のサーバが見つかっています |
| Status が not found | サーバの DLL が見つかっていません。パッケージを導入し直すと戻ります |
クライアントの設定
Section titled “クライアントの設定”設定ファイルもキーもクライアントごとに異なるため、8 種類のクライアントそれぞれに合わせた設定を生成します。
| クライアント | 設定ファイル | キー | 書式 | 補足 |
|---|---|---|---|---|
| Claude Code | .mcp.json | mcpServers | JSON | |
| GitHub Copilot CLI | .mcp.json | mcpServers | JSON | Claude Code と同じファイル |
| Visual Studio | .mcp.json | servers | JSON | 同じファイルの別のキー |
| VS Code | .vscode/mcp.json | servers | JSON | |
| Cursor | .cursor/mcp.json | mcpServers | JSON | |
| Roo Code | .roo/mcp.json | mcpServers | JSON | |
| Gemini CLI | .gemini/settings.json | mcpServers | JSON | ほかの設定と同じファイル |
| Codex CLI | .codex/config.toml | mcp_servers | TOML | プロジェクトの信頼が別に必要 |
手動で設定する場合
Section titled “手動で設定する場合”同じ節のドロップダウンでクライアントを選ぶと、貼り付ける内容がそのまま出ます(2 つのパスにはそのプロジェクトでの絶対パスが入ります)。
{ "mcpServers": { "tsukimi": { "command": "dotnet", "args": ["<パッケージの場所>/Server~/TsukimiCode.Mcp.dll", "<プロジェクトの場所>"] } }}Codex CLI だけ TOML です。
[mcp_servers.tsukimi]command = "dotnet"args = ["<パッケージの場所>/Server~/TsukimiCode.Mcp.dll", "<プロジェクトの場所>"]| 引数のプロジェクトの場所 | サーバが、そのプロジェクトに生成された実行環境の API のデータベースを読むために必要です |
| 省略した場合 | API の存在確認が行われません |
作業完了時のコンパイル検査
Section titled “作業完了時のコンパイル検査”Install Check を押すと、.claude/settings.local.json にフックが登録されます。このフックは、AI が作業完了を報告しようとした時点で、そのセッションで変更された T# のソースを 1 回だけコンパイルします。
| 場合 | どうなるか |
|---|---|
| コンパイルが通らなかった | 診断を提示して作業の続行を求めます |
| 警告だけが出た | 戻しません |
| コンパイルできた | 何も言いません。ウィンドウの Last check に最後の結果が出ます |
| 検査そのものが動かない | 止めずに通します。壊れた検査が作業を止め続けないためです |
| 同じ診断が 2 回続いた | 通します。修正できないと判断して打ち切ります |
| 対象のクライアント | Claude Code だけです。フック機能を持つクライアントが他に無いためです |
| 書き込み先 | 共有向けでない設定ファイルです。中身に絶対パスが入るためです |
| Remove | このフックだけを削除します。同じファイルの他の設定は保持されます |

6 つあります。
| ツール | 内容 |
|---|---|
ping | サーバが起動しているかの確認 |
inspect | ソースのコンパイルと、診断・解析結果の返却 |
test | [TsukimiTest] を付けたメソッドの実行 |
diagnostics | エラー番号の一覧 |
spec | この言語の説明の生成 |
decompile | コンパイルされたアセンブリを読める形にする |
中心になるのは inspect と test で、ping は接続の確認、diagnostics と spec は inspect が返した内容の解釈、decompile はすでにあるプログラムの確認に使います。
サーバが起動しているかどうかだけを確認します。引数はありません。
"tsukimi-mcp ok (static-analysis MCP; liveness check)"登録した直後に、設定が有効かどうかを切り分けるために使います。
inspect
Section titled “inspect”ソースをコンパイルして、結果を構造化した JSON で返します。
| 引数 | 内容 |
|---|---|
source | ソース 1 ファイル分の全文 |
file | 診断の位置に使うファイル名。省略すると <inline> |
sources | 複数のファイル。{name, text} の配列。source より優先される |
cost | 命令数とヒープ使用量を測定するかどうか。省略すると測定しません。測定時はビルド時と同じ最適化を実行するため、この呼び出しの所要時間の大半はここに費やされます |
1 つのソースが宣言できる具象の Behaviour は 1 つだけなので、次のような形を確認したいときは sources にまとめて渡します。
- インタフェースを Behaviour で実装する
- 基底の Behaviour と派生の Behaviour に分ける
- ある Behaviour から別の Behaviour のメソッドを呼ぶ
- Behaviour の型でコンポーネントを受け取る
1 つの source に 2 つ書いてエラーになるのは渡し方の誤りであり、言語の制限ではありません(sources で渡した場合、結果は Behaviour ごとに分かれ、最上位ではなく programs の各要素に入ります)。
コンパイルできたときの返り値
Section titled “コンパイルできたときの返り値”次のソースを source に渡し、cost を真にした結果です。cost を指定しないと、heap と cost の 2 つはキー自体が出力されません。
using UnityEngine;using Tsukimi;
public class Lamp : TsukimiBehaviour{ [SerializeField] private Light target; [UdonSynced] private bool on;
public override void Interact() { on = !on; target.enabled = on; RequestSerialization(); }}{ "schemaVersion": 1, "compiles": true, "diagnostics": [], "facts": { "heap": { "slots": 11, "limit": 1048576, "remaining": 1048565 }, "cost": { "instructionCount": 12, "staticExternCount": 3, "nsPerExtern": 77, "nsPerNonExtern": 5.4, "isStraightLine": true }, "entries": [ "Interact" ], "recursiveMethods": [], "loopCount": 0, "methods": [ { "name": "Interact", "isEntry": true, "isRecursive": false, "params": 0, "locals": 1, "loops": 0, "externs": 3, "entryName": "_interact" } ], "runtimeRisk": [], "contract": { "synced": [ { "name": "on", "type": "SystemBoolean", "sync": "none" } ], "exportedFields": [], "syncMode": "any" }, "nullDerefRisks": [ { "kind": "unassignedFieldDeref", "severity": "warning", "origin": "runtime", "message": "The reference field 'target' (type UnityEngineLight) is shown in the Unity Inspector and is never assigned in code, so its value comes only from there. ...", "method": "Interact", "field": "target", "type": "UnityEngineLight", "deref": "UnityEngineLight.__set_enabled__SystemBoolean__SystemVoid" } ] }, "summary": { "total": 0, "bySeverity": {}, "byOrigin": {} }, "externDb": { "resolved": true, "unknownApiChecks": true, "externCount": 32898 }}| 読みどころ | |
|---|---|
diagnostics が空・compiles が真 | このソースは変換できています |
nullDerefRisks に 1 件 | target はコードのどこでも代入されておらず、値は Inspector からしか入りません。割り当てを忘れると実行時に null のままになり、target.enabled への代入でイベント全体が中断されます(Udon には例外処理がないので、ログにも何も出ません) |
cost の nsPerExtern と nsPerNonExtern | extern の呼び出し 1 回と、それ以外の命令 1 つのおおよその所要時間です。命令数そのものより、extern を何回呼ぶかのほうが影響することが、この 2 つの差から読み取れます |
エラーのときの返り値
Section titled “エラーのときの返り値”Udon に存在しない型を使った例です。
using System.Collections.Generic;using UnityEngine;using Tsukimi;
public class Bag : TsukimiBehaviour{ private List<int> items;
public override void Interact() { items = new List<int>(); items.Add(1); Debug.Log(items.Count); }}{ "schemaVersion": 1, "compiles": false, "diagnostics": [ { "code": "TUKI0102", "severity": "error", "message": "Variable type does not exist in Udon: field items has the type SystemCollectionsGenericList", "explain": "This type does not exist in Udon (outside the extern database).", "location": { "file": "Bag.cs", "startLine": 7, "startCol": 23, "endLine": 7, "endCol": 23 }, "fix": "This type cannot be used here (it is outside the usable Udon extern set). ... For lists and dictionaries, use the VRChat data containers DataList / DataDictionary (VRC.SDK3.Data) - generic List<T> / Dictionary<K,V> are not usable.", "witness": "SystemCollectionsGenericList", "origin": "subset", "path": null }, { "code": "TUKI0101", "severity": "error", "message": "Not exposed to Udon: SystemCollectionsGenericList.__ctor____SystemCollectionsGenericList", "explain": "This API is not exposed to Udon.", "location": { "file": "Bag.cs", "startLine": 11, "startCol": 17, "endLine": 11, "endCol": 17 }, "fix": "This API is not exposed to Udon. Use an API that exists in the Udon extern set (see the `spec` tool, topic \"api\").", "witness": "SystemCollectionsGenericList.__ctor____SystemCollectionsGenericList", "origin": "subset", "path": null } ], "facts": null, "summary": { "total": 4, "bySeverity": { "error": 4 }, "byOrigin": { "subset": 4 } }, "externDb": { "resolved": true, "unknownApiChecks": true, "externCount": 32898 }}| 読みどころ | |
|---|---|
facts が null | compiles が偽のときは常に null です。コンパイルできていない以上、ヒープ消費もコストも測定できないためです。facts は存在するのに heap と cost だけが無い場合は別で、cost を指定していないことを意味します |
diagnostics が 2 件 | 4 件のうち先頭の 2 件だけを載せています(残りは List のメソッド呼び出しに対する同種の指摘)。全体の件数は summary.total にあります |
witness | エラーと判定した根拠となった識別子が入ります。この例では型名 SystemCollectionsGenericList と、見つからなかった extern の署名で、メッセージを読み解かなくても何が無かったのか分かります |
返り値のキー
Section titled “返り値のキー”| キー | 内容 |
|---|---|
schemaVersion | この出力形式のバージョン。形式が変わると上がる |
compiles | Udon のバイトコードまで変換できたか。実行環境で動作することの保証ではない |
diagnostics | エラーと警告の配列。番号・重大度・位置・直し方・原因の区分が入る |
summary | diagnostics を重大度と原因の区分で数えたもの |
facts | 変換できたときだけ入る。エラーのときは null |
facts.heap | 使うヒープの数と、残り。limit は実行環境がヒープに許す最大の数です。cost を真で渡したときだけ入ります |
facts.cost | 命令数・extern の呼び出し数と、1 回あたりのおおよその所要時間。cost を真で渡したときだけ入ります。入っていないのは測っていないという意味で、費用が無いという意味ではありません |
facts.entries | 実行環境から呼ばれるエントリポイントの名前 |
facts.methods | メソッドごとの内訳。ローカル変数・ループ・extern の数 |
facts.contract | 同期するフィールドと、外へ公開しているフィールドと、その Behaviour 全体の同期方式。方式はフィールドごとの sync とは別の軸です |
facts.runtimeRisk | コンパイルは通るが実行時に停止しうる箇所 |
facts.nullDerefRisks | null になりうる参照をそのまま辿っている箇所 |
externDb | 実行環境の API のデータベースを参照できたか。参照できた API の総数 |
診断に付く原因の区分
Section titled “診断に付く原因の区分”diagnostics の各要素には origin が付き、4 つの区分によって修正すべき場所が変わります(runtime はこの表には含まれず、facts.runtimeRisk と facts.nullDerefRisks にのみ付きます)。
| 区分 | 意味 |
|---|---|
subset | この言語が対応していない構文。書き方を変えればコンパイルできる |
environment | 実行環境に存在しない。書き方を変えてもコンパイルできない |
cost | コンパイルはできるが、コストが上限を超える |
tool | コンパイラか静的解析の内部エラー。修正すべき場所は書いたコードではない |
tool だけは性質が異なり、コンパイラ側の失敗を示すため、コードを書き直しても結果は変わりません。
ソースをコンパイルして、[TsukimiTest] を付けたメソッドを Unity なしで実行します。
受け取るのは 3 つです。
| 引数 | 内容 |
|---|---|
sources | まとめてコンパイルするファイル。inspect と同じ形。テストは 名前.Tests.cs に書いて、確認したい Behaviour と一緒に渡します(書き方は テスト) |
projectPath | Unity プロジェクトの場所。省略可。渡すと同じ内容がそのプロジェクトの中にも書かれ、Unity へ戻ってウィンドウを開き直すとそこに出ます |
fast | 素の C# として実行するかどうか。省略すると既定の道で実行します。速いので書いている途中の確認に向きますが、答える問いが違います——書いた規則が正しいかを確認し、コンパイルした形が正しいかは確認しません(テスト) |
{ "schemaVersion": 1, "origin": "mcp", "ranAt": "2026-08-13T05:57:52.7817272Z", "passed": 2, "failed": 1, "notRunnable": 0, "allPassed": false, "cases": [ { "name": "OneBumpAddsOne", "source": "Counter.Tests.cs", "outcome": "passed" }, { "name": "TwoBumpsMakeTwo", "source": "Counter.Tests.cs", "outcome": "passed" }, { "name": "DeliberatelyWrong", "source": "Counter.Tests.cs", "expected": "99", "actual": "1", "location": "32:9", "outcome": "assertFailed" } ]}| キー | 読み方 |
|---|---|
outcome | 4 種類あります。passed は成功、assertFailed は Assert が成立しなかったもの、halted は実行環境でも同じ箇所で停止するもの、notRunnable は実行そのものができなかったものです。notRunnable はテストの失敗ではなく、コードを書き直しても結果は変わらないため、まず detail を読んでください |
allPassed | テストが 1 本も見つからなかった場合と、1 本でも実行不可があった場合も偽になります。どちらも成功と誤認されないようにするためです |
origin | どちらの道で実行したかが値で入ります。mcp は既定の道、mcp-fast は素の C# の道、editor は Unity のウィンドウからの実行です。失敗を読む前にここを見ます |
trace | 失敗したときだけ入ります。実行中に記録された事象が時系列で並びます |
diagnostics
Section titled “diagnostics”エラー番号の一覧を返します(引数はありません)。inspect が返した番号の意味を参照するために使います。
{ "code": "TUKI0101", "defaultSeverity": "error", "en": "This API is not exposed to Udon.", "fixTemplate": "This API is not exposed to Udon. Use an API that exists in the Udon extern set ...", "origin": "subset"}上は配列の 1 要素で、inspect が返す個々の診断と違って位置が入りません(番号そのものの定義を参照する表だからです)。
この言語の説明を Markdown で返します(topic で選べる話題は 4 つ)。
| 話題 | 内容 | 根拠 |
|---|---|---|
subset | 対応している形の規則。エラー番号ごとに、エラーになる条件と直し方が並ぶ | 診断の一覧から自動生成。コンパイラの版が変われば内容も変わる |
api | 実行環境が持っている型の名前の一覧。名前を参照できることと、その API が実際に使えることは別で、使えるかどうかは inspect に通して確認する | extern データベースから自動生成。同上 |
pitfalls | 通常の C# と挙動が違うところ。null の発生源、イベントが黙って終わるケースなど | 手書きの解説。コンパイラの定義からは自動生成できない内容 |
builtins | この言語固有の組み込み要素。属性・基底クラス・Assert・カーネル API。実行環境の API ではないので api には出ない | コンパイラが持つ型定義から自動生成 |
| (省略) | 全体の見取り図と話題の索引 | extern データベースを参照できているかどうかも書かれる。参照できていないと未知の API と未知の型の検査(TUKI0101・TUKI0102)が実行されないので、その状態でエラーになった形は、書き方ではなくデータベースが無いことが理由かもしれない |
| (知らない話題) | 使える話題を並べたメッセージ |
結果を読むときの注意
Section titled “結果を読むときの注意”compilesが真でも、実行環境で動作するとは限りません。バイトコードまで変換できたことだけを表しますruntimeRiskとnullDerefRisksが空でも安全ではありません。この検出は意図的に狭く、null を返しうる呼び出しの直後に連ねた形しか追っていません。いったんローカル変数へ代入すると、そこで追跡が切れます。コンポーネントの取得や実行環境から取る値は、結果の内容にかかわらず自分で確認してくださいexternDb.resolvedが偽のときは、未知の API と未知の型の検査が実行されていません。この状態では、データベースを参照できないせいでエラーになっている形が混ざります- 重大度が
infoの診断は止める理由になりません。compilesが真でも出ます
decompile
Section titled “decompile”コンパイルされた Udon のアセンブリを、C# に似た読める形にします。すでにワールドに置かれているプログラムの確認に使い、引数は 2 つです。
| 引数 | 内容 |
|---|---|
uasm | アセンブリのテキスト |
file | アセンブリファイルのパス。uasm を省略したときだけ読まれます |
{ "schemaVersion": 1, "file": "Lamp.uasm", "source": "public class Lamp\n{\n ...\n}", "fidelity": { "isOriginalSource": false, "isCompileChecked": false, "receiverIsInferred": true, "note": "This is a readable rendering of the assembly you passed in. ..." }, "externDb": { "resolved": true, "externCount": 32898 }, "warnings": [], "facts": { "heap": { "slots": 11, "limit": 1048576, "remaining": 1048565 }, "cost": { "instructionCount": 42, "staticExternCount": 6, "nsPerExtern": 77, "nsPerNonExtern": 5.4 }, "entries": ["_interact"], "externs": ["UnityEngineGameObject.__SetActive__SystemBoolean__SystemVoid"], "loopCount": 0 }}| キー | 読み方 |
|---|---|
source | 可読化した結果です。元のソースではありません。ローカル変数名・クラス名・ブロック構造は、アセンブリだけから再構成したものです(アセンブリはそれらを持っていません) |
fidelity | このツールの出力の性質を示す固定値です。入力ごとに判定するものではなく、常に同じ値です。source より先に読みます |
externDb | 呼び出しの受け手を、データベースを参照して決めたか、周囲の命令列から推定したかが分かります。推定は間違うことがあり、間違った結果は普通のコードに見えます |
warnings | 結果が信用できない箇所です。receiverNotResolved は、ドットの左側に置いた式がその呼び出しのレシーバとして成立しないもの、notRaisedToStatements は、スタックの操作のまま残した行です。空でも、すべてが正しく出たという意味にはなりません |
facts | 渡したアセンブリから測った値です。heap は inspect と同じ数え方で、cost は命令数と静的な外部呼び出しの数です |
facts.entries | アセンブリに定義されたエントリポイントの名前です。inspect の同じキーが C# の名前(Interact)を返すのに対し、こちらは実行環境が呼ぶ名前(_interact)を返します |
facts.cost.isStraightLine | ありません。アセンブリからはループが見えても再帰は見えないため、loopCount が数えるのは後ろ向きのジャンプだけです |
| 自分で書いたソース | inspect を使います。このツールの出力を inspect にかけ直す使い方は想定していません |
ping を呼ぶと、次の 1 行が返ります。
"tsukimi-mcp ok (static-analysis MCP; liveness check)"クライアント側にも、接続しているサーバの一覧があります。

| 見えるツール | 6 つ(ping・inspect・test・diagnostics・spec・decompile) |
| 1 つも見えないとき | 設定を書き込む前にクライアントを起動したか、プロジェクトの外で起動しています |
データベースの確認
Section titled “データベースの確認”短いソースを 1 つ inspect にかけると、externDb が返ります。
"externDb": { "resolved": true, "unknownApiChecks": true, "externCount": 32898 }resolved | 真なら参照できています。偽の間は、実在する API も未知として報告されます |
| 偽になる条件 | Unity でこのプロジェクトをまだ一度も開いていないときです |
externCount | 参照できた API の数。導入した SDK の版で変わるので、手元の値が上と違っていても構いません |
併用をおすすめする MCP サーバ
Section titled “併用をおすすめする MCP サーバ”シーンへの配置・Inspector での参照の割り当て・Play モードでの確認はこのサーバの対象外で、これらも AI に操作させたい場合は、Unity エディタを外部から操作する MCP サーバを併用します。
この種のサーバは第三者が公開しているもので、このパッケージには含まれません(MCP for Unity の場合は、Unity の Package Manager の Add package from git URL に次のアドレスを入力します)。
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#mainうまくいかない場合
Section titled “うまくいかない場合”| 症状 | 原因 | 対処 |
|---|---|---|
| メニューに TsukimiCode が出ない | パッケージが入っていないか、プロジェクトのコンパイルが通っていない | Unity のコンソールを読む。コンパイルエラーが出ていれば、そちらが先 |
| クライアントに tsukimi のツールが出ない | 設定の前に起動していたか、プロジェクトの外で起動している | プロジェクトの直下で再起動する。ウィンドウで該当クライアントが configured になっているかを見る |
| Configure を押したのに変わらない | 起動中のクライアントは、設定を起動時にしか読まない | クライアントを再起動する |
| Install Skill を押したのに AI が手順書を知らない | インストールした Skill も、クライアントは起動時にしか読まない | クライアントを再起動する |
| AI が Skill を参照せずにコードを書いている | Skill がクライアントに読み込まれていない | ウィンドウの Grounding が up to date かを確認し、押し直してから再起動する |
| Codex CLI だけ接続できない | プロジェクトが信頼されていない | そのクライアントの手順で信頼させてから、再起動する |
| パッケージの更新後に接続できない | 設定に書かれたサーバの場所が、更新で変わった | Configure と Install Skill を押し直して、クライアントを再起動する |
| 更新したのにサーバの挙動が古い | クライアントが古いサーバを動かしたまま | クライアントを再起動する(サーバも一緒に再起動される) |
| Server の Status が not found | 同梱のサーバ DLL が見つからない | パッケージを導入し直す |
| 実在するはずの API が無いと言われ続ける | 実行環境の API のデータベースが生成されていない | Unity でこのプロジェクトを一度開く。externDb.resolved が偽の間はこの状態 |
| 完了時の検査が動作しているか分からない | コンパイルできたときは何も出ない | ウィンドウの Last check を見る。欄が out of date なら Install Check を押し直す |
| AI がファイルを置いたのに Unity で何も起きない | Unity は、フォーカスが戻るまで外で置かれたファイルをインポートしない | Unity のウィンドウを 1 度クリックする |
| シーンで触っても何も起きない | Inspector の参照が空 | UdonBehaviour の欄に割り当てる。Udon には例外処理がないので、ログには何も出ない |
どの行の対処でも解決しないときは、ウィンドウを開き直して各節の表示を上から順に確認し、最初に異常が出ている節から対処してください。