MCP server
A server that lets an AI client call the compiler itself. It ships inside the package, and the client launches it according to its configuration.
The setup window
Section titled “The setup window”Open Tsukimi MCP from the TsukimiCode menu in Unity. Check the clients you use and press the main button, and the configuration of every checked client, the Skill, and the final check (a compile check run when work is reported as done) are written together.

| Location | Contents |
|---|---|
| Toolbar | The package version, and Refresh, which rereads the configuration files and updates the marks |
| Client rows | 8 rows. Each has a checkbox, the location of the configuration file, and a mark. The checkboxes are saved per project in UserSettings/TsukimiMcpClients.json. Until something is saved, Claude Code and any client whose configuration already has a tsukimi entry are checked |
| Server | Whether the bundled server DLL is found |
| Skill | State of the instructions the AI reads (Skills) |
| Final check | State of the hook that checks compilation when work is reported as done. Claude Code only |
| Main button | Writes the configuration of the checked clients, the Skill, and the final check together. The label changes with the state |
| More | The start command, the note in AGENTS.md, the configuration to paste by hand, and removal of the final check. Closed at first |
| Bottom row | Last check (the most recent result of the final check), and Open the guide, which opens the setup page |
Row marks
Section titled “Row marks”| Mark | Meaning |
|---|---|
| ✓ | Installed and up to date |
| Empty circle | Not installed yet |
| ! | Installed but out of date. Either an earlier version wrote it, or it points at a different server or project |
| ✕ | Cannot be installed. Appears when the bundled server or the source files of the Skill cannot be found |
Status text
Section titled “Status text”| Rows | Text | Meaning |
|---|---|---|
| Server | found | The bundled server is found |
| Server | not found · reinstall the package to restore it | The server DLL is not found. The main button cannot be pressed either. Reinstalling the package restores it |
| Skill | up to date · shared by every client | Up to date. Every client reads the same Skill |
| Skill | out of date · shared by every client | An earlier version of the Skill is installed |
| Skill | out of date (changed by hand) · shared by every client | An earlier version of the Skill is installed, and some files were changed by hand after installation |
| Skill | not installed · shared by every client | Not installed |
| Skill | source not found | The source files of the Skill are missing from the package |
| Final check | up to date · Claude Code only | Up to date |
| Final check | out of date · Claude Code only | It points at a different server or project |
| Final check | not installed · Claude Code only | Not installed |
Main button
Section titled “Main button”| Text | When it appears | Note under the button |
|---|---|---|
| Set Up (N clients) | At least one of the checked clients, the Skill, and the final check is not installed yet or cannot be installed | Writes the checked clients, the Skill and the final check. Restart your AI client afterwards. |
| Update (N clients) | Everything is installed, and something is out of date | The package was updated. Restart your AI clients afterwards. |
| Set Up Again | Everything is up to date | Everything is in place. |
| Item | Description |
|---|---|
| N | The number of checked clients (client when it is 1) |
| What goes into the label | The checked clients, the Skill, and the final check. The final check is included only when Claude Code is checked |
| What happens when pressed | The same for every label. Writes the configuration of the checked clients and the Skill, and also the final check if Claude Code is checked. The Skill is installed regardless of the checkboxes |
| Things already up to date | These are rewritten too. Set Up Again is for bringing the way they are written in line with the current version |
| When it cannot be pressed | When Server is not found |
| After pressing | Restart the AI client. Clients read the configuration and the Skill only at startup |
Confirmations during writing
Section titled “Confirmations during writing”| Case | Text | What happens when you choose |
|---|---|---|
| The configuration file cannot be read as JSON | ... could not be read as JSON. Replace it with a file that holds only the tsukimi entry? | Replace makes the file hold only the tsukimi entry. With Cancel, that client is not written |
| A file with the same name as one in the Skill exists with different contents | These files already exist and differ. Replace them?, followed by the list of those files | Replace overwrites them. With Cancel, the Skill is not installed |
.claude/settings.local.json cannot be read as JSON | ... could not be read as JSON, so it was left alone. | The final check is not installed. The file is left as it is |
Server
Section titled “Server”| found | The bundled server is present |
| not found | The server DLL is not found. The main button cannot be pressed and the mark is ✕. Reinstalling the package restores it |
| Start command | The command the client runs appears under Start command in More |
Client configuration
Section titled “Client configuration”The configuration file and the key differ per client, so 8 variants are written out separately.
| Client | Configuration file | Key | Format | Path to the server | Notes |
|---|---|---|---|---|---|
| Claude Code | .mcp.json | mcpServers | JSON | Path relative to the project | |
| GitHub Copilot CLI | .mcp.json | mcpServers | JSON | Path relative to the project | The same file as Claude Code. It is launched from the project folder |
| Visual Studio | .mcp.json | servers | JSON | Absolute paths (the project location is written too) | A different key in the same file |
| VS Code | .vscode/mcp.json | servers | JSON | Path starting with ${workspaceFolder} | |
| Cursor | .cursor/mcp.json | mcpServers | JSON | Path starting with ${workspaceFolder} | |
| Roo Code | .roo/mcp.json | mcpServers | JSON | Path relative to the project | |
| Gemini CLI | .gemini/settings.json | mcpServers | JSON | Absolute paths (the project location is written too) | The same file as its other settings |
| Codex CLI | .codex/config.toml | mcp_servers | TOML | Absolute paths (the project location is written too) | The project must be trusted separately |
| Item | Description |
|---|---|
Clients written with a relative path or ${workspaceFolder} | Only the path to the server is written, not the project location. The server walks up from its own file and uses the first folder that contains both Assets and ProjectSettings as the project. So the same configuration starts the server after a clone to another machine or folder |
| Clients written with absolute paths | The absolute paths of both the server and the project are written |
| When the package is placed outside the project | Absolute paths are written for every client |
| How a client’s mark is decided | By where the path in the configuration points. Even if it is written differently from what the window writes, it is ✓ as long as it points at the current server and this project |
| Absolute paths written by an earlier version of the window | Earlier versions wrote absolute paths for every client. If they point at the current server and project, ✓ appears and they are not rewritten automatically. To move to a relative path or ${workspaceFolder}, press the main button (Set Up Again when everything is ✓) |
Writing the settings by hand
Section titled “Writing the settings by hand”Open More and choose a client in the Paste by hand dropdown, and what to paste appears as it is.
{ "mcpServers": { "tsukimi": { "command": "dotnet", "args": ["<package location relative to the project>/Server~/TsukimiCode.Mcp.dll"] } }}{ "servers": { "tsukimi": { "command": "dotnet", "args": ["${workspaceFolder}/<package location relative to the project>/Server~/TsukimiCode.Mcp.dll"] } }}[mcp_servers.tsukimi]command = "dotnet"args = ["<package location>/Server~/TsukimiCode.Mcp.dll", "<project location>"]| Item | Description |
|---|---|
| What appears | The same as what the window writes. The paths hold the values for that project |
| The examples above | In order: Claude Code (relative path), VS Code (${workspaceFolder}, key servers), and Codex CLI (absolute paths, TOML) |
| The project location argument | Present only for clients written with absolute paths. The server uses it to read the database of the runtime’s APIs generated for that project |
| If it is omitted | The server walks up from its own file to find the project. If the server is outside the project and cannot find it, APIs are not checked for existence |
Rewriting after the package is updated
Section titled “Rewriting after the package is updated”| What it runs | What happens |
|---|---|
| When | When Unity loads scripts (at startup, or after the package is updated). Only when the recorded version of the Skill installed from the window is an earlier one; nothing happens for the same version |
| Skill | Rewrites the files that are still as they were installed (and untouched) (Skills) |
| Skill files changed by hand | Left as they are, and named in a warning in the Console. To replace them, press the main button and choose Replace in the prompt about files with the same name |
| Client configuration | Rewrites only the entries where tsukimi points at a different server or a different project |
| Final check | Rewritten only when it is installed and out of date |
| Projects with no record of the Skill | Nothing is rewritten. A Skill installed from a window older than 0.9.0 has no record, so reinstall it once with the main button |
| After rewriting | One line appears in the Console: Updated ... for TsukimiSharp <version>. Restart your AI client so that it reads them. Restart the client |
| When nothing needed rewriting | If the version differs from the one this project loaded last time, and the configuration of any client has a tsukimi entry, the Console shows the line TsukimiSharp <version> is loaded. If your AI client was already running, restart it, or reconnect its MCP servers, so that it uses this version. Restart the client, or reconnect its MCP servers |
Open More under the card to see 4 fields that are not used day to day.
| Field | Button | Contents |
|---|---|---|
| Start command | Copy | The same start command that goes into the Claude Code configuration. When the package is inside the project, the path to the server is relative, so run it from the project folder when starting it by hand |
| Note in AGENTS.md | Add | Adds a short note to AGENTS.md, which is always read (Skills) |
| Paste by hand | Copy | What to paste into the configuration of the client chosen in the dropdown |
| Final check | Remove | Removes the final check hook. Cannot be pressed when it is not installed |

The compile check when work is reported as done
Section titled “The compile check when work is reported as done”Check Claude Code and press the main button, and a hook is registered in .claude/settings.local.json. When the AI is about to report that work is done, the hook compiles the T# sources changed in that session, once.
| Case | What happens |
|---|---|
| Compilation failed | Hands back the diagnostics and asks for the work to continue |
| Only warnings appeared | Doesn’t send it back |
| Compilation succeeded | Says nothing. The last result appears under Last check in the window |
| The check itself doesn’t run | Lets it through rather than stopping. A broken check must not keep blocking work |
| The same diagnostic twice in a row | Lets it through. It has decided the problem cannot be fixed and stops |
| Which client this covers | Claude Code only. The hook mechanism exists only there |
| Where it is written | A configuration file not meant for sharing, because absolute paths go into it |
| Last result | It appears under Last check at the bottom of the window. It reads not run yet if the check has never run |
| Deletion | Remove under Final check in More deletes only this hook. Other settings in the same file are kept |
There are seven.
| Tools | Description |
|---|---|
ping | Check whether the server is running |
inspect | Compile source and return diagnostics and facts |
test | Run the methods marked with [TsukimiTest] |
diagnostics | List of error numbers |
spec | Generate a description of this language |
decompile | Turn a compiled assembly into a readable form |
profile | Reading the instruction and extern counts of the programs in the scene open in Unity |
inspect and test are the core; ping checks the connection, diagnostics and spec interpret what inspect returned, and decompile is for examining a program that already exists.
Checks whether the server is running, and which version is running. It takes no arguments.
"tsukimi-mcp ok (version x.y.z; static-analysis MCP; liveness check)"The version of the running server goes where x.y.z is. Use it right after registering, to tell whether the configuration is in effect.
When the server started before a package update is still running after it, tools other than ping do not run, and the following message comes back as an error (isError: true). (ping adds the same message after its line, separated by one space, and does not make it an error.)
The TsukimiSharp package was updated after this MCP server started (running x.y.z, installed x.y.z). Restart your AI client, or reconnect its MCP servers, to use the new version.installed is the version of the updated package, and is left out when it cannot be read. Restarting the client, or reconnecting its MCP servers in the client, starts the updated server.
inspect
Section titled “inspect”Compiles the source and returns the result as structured JSON.
Arguments
Section titled “Arguments”| Arguments | Description |
|---|---|
source | The full text of one source file |
file | The file name used for diagnostic locations. <inline> when omitted |
sources | Multiple files. An array of {name, text}. Takes precedence over source |
path | Files in the Unity project, passed by path instead of by their text: one path or an array, relative to the project folder (such as Assets/Lamp/Lamp.cs). A .cs file is compiled together with the files Unity collects when it compiles that program (the files in the same folder and the folders below it, and the files in TsukimiShared folders, leaving out anything under a folder named Editor). A program asset (.asset) is treated as the .cs file it compiles, and a folder passes every .cs file below it. The files are read as saved on disk, so for a file with unsaved changes, pass its text as well. Text whose name matches a file read from path is used in place of that file |
mochi | Checks a MoCHI .mochi.xml file instead of .cs. See MoCHI inspect for the details |
cost | Whether to measure instruction count and heap. Omitted, nothing is measured. Measuring runs the same optimization as shipping does, so most of this call’s time goes there |
One source can declare only one concrete Behaviour, so pass several at once through sources when you want to check a shape like the one below.
- Implementing an interface with a Behaviour
- Splitting into a base Behaviour and a derived Behaviour
- Calling a method on another Behaviour from one Behaviour
- Receiving a component as a Behaviour type
Writing two in a single source and getting an error is a mistake in how you passed them, not something the language can’t do (pass them through sources and the results are split per Behaviour, landing in each element of programs rather than at the level above).
What comes back on a successful compile
Section titled “What comes back on a successful compile”This is the result of passing the following source as source with cost set to true. Without cost, the two keys heap and cost are absent altogether.
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 }}| What to look at | |
|---|---|
diagnostics empty, compiles true | This source compiles |
One entry in nullDerefRisks | target is never assigned anywhere in the code; its value can only come from the Inspector. Forget to assign it and it stays null at runtime, and the assignment to target.enabled silently cuts the whole event short (Udon has no exception handling, so nothing appears in the log either) |
nsPerExtern and nsPerNonExtern under cost | Roughly how long one extern call and one non-extern instruction take. The gap between the two shows that how many times you call an extern matters more than the instruction count itself |
What comes back on an error
Section titled “What comes back on an error”This is an example that uses a type that does not exist in 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 }}| What to look at | |
|---|---|
facts is null | This is what you get when compiles is false. Nothing compiled, so neither heap usage nor cost can be measured. When facts is there but only heap and cost are missing, that’s a different matter: it means cost wasn’t passed |
Two entries in diagnostics | Only the first 2 of 4 are shown (the rest are the same kind of finding against List method calls). The overall count is in summary.total |
witness | It holds the actual evidence behind the decision to call it an error. Here that is the type name SystemCollectionsGenericList and the signature of the extern that wasn’t found, so you can see what was missing without unpacking the message |
Keys in the return value
Section titled “Keys in the return value”| Key | Description |
|---|---|
schemaVersion | The version of this report’s shape. It goes up when the shape changes |
compiles | Whether it compiled all the way to Udon bytecode. Not a guarantee that it works in the runtime |
diagnostics | An array of errors and warnings. Holds the number, severity, location, how to fix it, and the origin category |
summary | diagnostics counted by severity and by origin category |
facts | Present only when it compiled. Null on an error |
facts.heap | The number of heap slots used, and how many remain. limit is the maximum the runtime allows for the heap. Present only when cost was passed as true |
facts.cost | Instruction count, extern call count, and the approximate time each one takes. Present only when cost was passed as true. Its absence means it wasn’t measured, not that there is no cost |
facts.entries | The names of the entry points the runtime calls |
facts.methods | A per-method breakdown. Counts of local variables, loops, and externs |
facts.recursiveMethods | The names of the methods that may be entered again while they are still running, and so save their arguments and local variables around the call (Entering a method again while it runs) |
facts.contract | Synced fields, fields exposed outward, and the sync method of the behaviour as a whole. The method is a separate axis from the per-field sync |
facts.runtimeRisk | Forms that compile but can halt at runtime |
facts.nullDerefRisks | Places where a reference that can be null is dereferenced directly |
externDb | Whether the database of runtime APIs could be read, and how many APIs it holds |
The origin category attached to a diagnostic
Section titled “The origin category attached to a diagnostic”Each element of diagnostics carries an origin, and its 4 categories change where the fix belongs (runtime isn’t in this table; it only appears on the facts.runtimeRisk and facts.nullDerefRisks side).
| Category | Meaning |
|---|---|
subset | A form this language doesn’t accept. Rewriting the form makes it compile |
environment | Doesn’t exist on the runtime side. Rewriting the form won’t make it compile |
cost | It converts, but the cost exceeds the limit |
tool | The compiler or the analysis failed. The place to fix is not the code you wrote |
tool alone is different in kind: it signals that the compiler failed rather than the code you wrote, so rewriting the code gives the same result.
Compiles the source and runs the methods marked with [TsukimiTest] without Unity.
It takes five arguments.
| Arguments | Description |
|---|---|
sources | The files to compile together. Same shape as inspect. Write tests in Name.Tests.cs and pass them along with what you want to check (see Testing for how) |
path | The same as for inspect. Passing a .cs file also reads its Name.Tests.cs. For a file with unsaved changes, pass its text in sources as well |
projectPath | The location of the Unity project. Optional. Pass it and the same content is also written inside that project, appearing there when you return to Unity and reopen the window. path is also read from this project |
fast | Whether to run as plain C#. Omitted, it runs on the default path. It’s fast, so it suits checking while you’re still writing, but it answers a different question: it checks whether the rules you wrote are right, not whether the compiled form is right (Testing) |
summary | When true, only the tests that did not pass are returned in cases. When omitted, all of them are returned. passed, failed, notRunnable and allPassed count every test, and the number of passed tests left out is in passedOmitted. The result written under projectPath keeps every test |
Return value
Section titled “Return value”{ "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" } ]}| Key | How to read it |
|---|---|
outcome | It splits four ways. assertFailed is a check that didn’t hold, halted is a form that stops at the same place in the runtime, and notRunnable is something that couldn’t be run at all, which is not the same as a test failing (rewriting the code won’t change the result, so read detail first) |
allPassed | It is also false when no test was found at all, and when even one test was not-runnable. Both are kept from looking green |
origin | Which path ran it, as a value. mcp is the default path, mcp-fast is the plain C# path, and editor is a run from the Unity window. Read this before reading a failure |
trace | Present only when the test failed. Events recorded during the run are listed in the order they happened |
passedOmitted | Present only when summary is passed as true. The number of passed tests left out of cases |
diagnostics
Section titled “diagnostics”Returns the list of error numbers (it takes no arguments). Use it to look up what a number returned by inspect means.
{ "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"}The above is one element of the array. Unlike the individual diagnostics inspect returns, it carries no position (this is a table defining the numbers themselves).
Returns documentation for this language as Markdown (topic offers 4 choices).
| Topic | Description | Basis |
|---|---|---|
subset | Rules for supported forms. For each error code, the conditions that trigger it and how to fix it | Generated from the diagnostic list. Its content changes when the compiler changes |
api | The list of type names the runtime holds. Being able to look a name up is not the same as that API being usable; run it through inspect to find out | Generated from the extern database. Same as above |
pitfalls | Where the behaviour differs from plain C#. Where null comes from, forms where an event ends silently, and so on | Written by hand. Facts that cannot be generated from the compiler’s own definitions |
builtins | The surface this language itself has: attributes, base classes, how to write checks, the kernel surface. Not runtime APIs, so it doesn’t appear in api | Generated from the type surface the compiler holds |
| (omitted) | The overall map, and an index of the topics | It also states whether the extern database could be read. If it couldn’t, the checks for unknown APIs and unknown types (TUKI0101 and TUKI0102) don’t run, so a form that errored in that state may have failed because the database was absent rather than because of how it was written |
| (an unknown topic) | A message listing the available topics |
Notes on reading the results
Section titled “Notes on reading the results”compilesbeing true does not mean it works in the runtime. It only means it compiled all the way to bytecode- Empty
runtimeRiskandnullDerefRisksdo not mean it is safe. This detection is deliberately narrow: it follows only forms chained directly onto a call that can return null. Assign to a local variable once and the tracking stops there. Check component lookups and values taken from the runtime yourself, whatever the result says - When
externDb.resolvedis false, the checks for unknown APIs and unknown types haven’t run. In that state, forms that error only because the database couldn’t be read are mixed in - A diagnostic with
infoseverity is not a reason to stop. It appears even whencompilesis true
decompile
Section titled “decompile”Turns a compiled Udon assembly into a readable form resembling C#. Use it to examine a program already placed in a world. It takes two arguments.
| Arguments | Description |
|---|---|
uasm | The assembly text |
file | The location of the assembly file. Read only when uasm is absent |
{ "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 }}| Key | How to read it |
|---|---|
source | The readable result. It is not the original source. Local variable names, class names, and block structure are reconstructed from the assembly alone (the assembly doesn’t carry them) |
fidelity | The nature of this tool. It isn’t a judgment per input, so the same value comes back every time. Read it before source |
externDb | Tells you whether the receiver of each call was decided by consulting the database or inferred from the surrounding shape. Inference can be wrong, and a wrong result looks like ordinary code |
warnings | Places where the result can’t be trusted. receiverNotResolved marks a call where what came before the dot can’t be that call’s receiver; notRaisedToStatements marks lines left as stack operations. Empty doesn’t mean everything came out right |
facts | Values measured from the assembly you passed. heap is counted the same way as in inspect; cost is the instruction count and the number of static external calls |
facts.entries | The names of the entry points written in the assembly. Where the same key in inspect returns the C# spelling (Interact), this one returns the name the runtime calls (_interact) |
facts.cost.isStraightLine | Absent. Loops are visible in an assembly but recursion isn’t, so loopCount counts only backward jumps |
| Source you wrote yourself | Use inspect. Feeding this tool’s output back into inspect is not an intended use |
profile
Section titled “profile”| What it returns | The instruction and extern counts of each program placed in the scene open in Unity. The counts are the ones Unity wrote to Library/Tsukimi/profile.json; this tool does not count again |
| When it is written | When you choose TsukimiCode > Tsukimi Profiler from the menu, and when Unity finishes compiling a saved TsukimiSharp script (Profiler) |
| Arguments | Only program. Pass the name of a program (programs[].name) to get that one program only. Omit it to get every program in the scene |
| When there is no file | It returns isError: true with a message telling you to choose the menu item or save a script |
What the summary contains
Section titled “What the summary contains”{ "schemaVersion": 1, "writtenAt": "2026-10-11T08:12:34.5678901Z", "scene": "SampleScene", "tsukimiVersion": "0.9.0", "programs": [ { "name": "ReactiveLamp", "asset": "Assets/ReactiveLamp.asset", "tsukimiSharp": true, "placed": 3, "instructions": 395, "externs": 45, "codeSizeBytes": 2944, "syncedCount": 0, "events": [ { "name": "_interact", "instructions": 129, "externs": 15, "perFrame": false, "measuredExterns": 7 }, { "name": "_start", "instructions": 49, "externs": 7, "perFrame": false, "measuredExterns": 4 } ], "stale": false } ]}| Key | How to read it |
|---|---|
writtenAt・scene・tsukimiVersion | The time the summary was written, the scene that was open then, and the package version |
placed | The number of UdonBehaviours in the scene that use the program |
instructions・externs | The instruction and extern counts of the whole program. They are counted the same way as in the Profiler |
events[].instructions・events[].externs | The instructions and externs reachable from that entry point. The bodies of the methods it calls are not included |
events[].perFrame | Whether the event runs every frame |
events[].measuredExterns | Present for TsukimiSharp programs only. The number of externs called when the entry point was run once with every field at its default value, including the calls made inside the methods it calls. With default values an early return on a condition is often taken, so this is not the count during actual play |
events[].notMeasured | For an entry point that could not be run, the reason is given instead of a number |
stale | Present for TsukimiSharp programs only. true means the asset on disk changed to a different program after it was counted. Choose the menu item again to count it again |
| Unit of the counts | Every number is a count, not a time. Nothing comes from play mode |
Checking the connection
Section titled “Checking the connection”Calling ping returns the following single line.
"tsukimi-mcp ok (version x.y.z; static-analysis MCP; liveness check)"The client has a list of the servers it is connected to as well.

| Tools visible | 7 (ping, inspect, test, diagnostics, spec, decompile, profile) |
| When none are visible | Either the client was launched before the configuration was written, or it was launched outside the project |
Checking the database
Section titled “Checking the database”Run one short source through inspect and externDb comes back.
"externDb": { "resolved": true, "unknownApiChecks": true, "externCount": 32898 }resolved | True means it could be read. While it is false, APIs that really exist are reported as missing |
| When it is false | When you have never once opened this project in Unity |
externCount | The number of APIs read. It varies with the SDK version you installed, so your number differing from the one above is fine |
Servers used alongside it
Section titled “Servers used alongside it”Placing things in a scene, assigning references in the Inspector, and checking with Play all lie outside this server; to let an AI touch those too, use an MCP server that drives the Unity editor from outside alongside it.
Servers of that kind are published by third parties and are not included in this package (for MCP for Unity, put the following address into Add package from git URL in Unity’s package manager).
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#mainTelling apart what went wrong
Section titled “Telling apart what went wrong”| Symptom | Cause | What to do |
|---|---|---|
| TsukimiCode doesn’t appear in the menu | Either the package isn’t installed, or the project doesn’t compile | Read Unity’s console. If there is a compile error, that comes first |
| The tsukimi tools don’t appear in the client | It was launched before the configuration, or launched outside the project | Restart it from the project root. In the window, check that the mark of that client is ✓ |
| Nothing changes after pressing the main button | A running client reads its configuration only at startup | Restart the client |
| The AI does not know the instructions even though the main button was pressed | The instructions that were written out are also read by the client only at startup | Restart the client |
| The AI writes code without consulting the Skill | The instructions haven’t been loaded by the client | Check in the window that the Skill row says up to date, press the main button, and then restart |
| Only Codex CLI can’t connect | The project isn’t trusted | Trust it by that client’s procedure, then restart |
| Only GitHub Copilot CLI cannot connect | The path to the server in the configuration is relative to the project, and the client is launched from another folder | Launch it again from the project folder |
| Can’t connect after updating the package | The location of the server written in the configuration changed with an update, and the automatic rewrite did not run (it does not run in projects with no record of a Skill installed from the window) | Open the window, press the main button if any row is marked !, and restart the client |
| After updating from 0.8.1, the Configure, Install Skill, and Install Check buttons are gone | In 0.9.0 the window was rebuilt around a single main button. A Skill installed with 0.8.1 has no record, so it is not rewritten automatically | Check the clients you use and press the main button once (choose Replace if the prompt about files with the same name appears). From the next update on, it is rewritten automatically |
| The server does not start in a cloned copy | The 0.8.1 window wrote absolute paths into the configuration. In the original project they point at the current server and project, so ✓ appears and they are not rewritten automatically | Press the main button in the original project (Set Up Again when everything is ✓) and the configuration is rewritten with a relative path or ${workspaceFolder}. Visual Studio, Gemini CLI, and Codex CLI keep absolute paths |
After a package update, tools return The TsukimiSharp package was updated after this MCP server started … | The client is still running the server it started before the update. When the server detects that its own files were updated, it returns this message instead of running the tool, so that it never runs old and new files together | Restart the client, or reconnect its MCP servers in the client. It is not a fault in the code or the tool |
After a package update, tools fail with Method not found or a similar error | The server started before the update is version 0.9.0 or earlier, which does not detect updates | Restart the client, or reconnect its MCP servers in the client |
| The Server row says not found | The bundled server DLL is not found. The main button cannot be pressed either | Install the package again |
| It keeps saying an API that should exist is missing | The database of runtime APIs hasn’t been generated | Open this project once in Unity. This is the state whenever externDb.resolved is false |
| No way to tell whether the completion check is running | Nothing appears when compilation succeeds | Look at Last check at the bottom of the window. If the Final check row is out of date, check Claude Code and press the main button |
| The AI placed a file but nothing happens in Unity | Unity doesn’t import a file placed from outside until focus returns to it | Click the Unity window once |
| Nothing happens when you touch it in the scene | The Inspector reference is empty | Assign it in the UdonBehaviour field. Udon has no exception handling, so nothing appears in the log |
If none of the fixes above solves it, press Refresh in the window and check the marks from the top, starting with the first row that is not ✓.