status / cancel / list_providers were near-identical across GenerateAudio,
GenerateImage and GenerateModel (differing only by a kind label + poll
interval), and NormalizeOutputFolder was a verbatim triple copy.
- New AssetGenToolHelpers.{Status,Cancel,ListProviders} — the three tools now
delegate, passing their kind label / poll interval.
- NormalizeOutputFolder moved to AssetGenPaths (it already lived on
TryGetAssetsFolder).
- Behaviour preserved (image `remove_background` arm kept; provider-list
filtering unchanged). Net -120 lines of duplication.
Verified: EditMode 1167 tests / 0 failures + in-editor smoke (audio/image/model
list_providers, status/cancel error paths, image remove_background all intact).
Claude-Session: https://claude.ai/code/session_015KYy51gwBuhDuLZXXoqc98
Phases 2-3. Adds a curated AssetGenModelCatalog (image/3D/audio models with
price/duration/use-case metadata; defaults reference the adapter constants so the
panel default equals what an omitted model resolves to). Adds the generate_audio
MCP tool + CLI, threads a per-(kind,provider) selected-model pref so a GUI choice
becomes the default generate_* uses when no model is passed, and makes
Tripo/Meshy consume req.Model. Per-provider prefs (not per-type) so disjoint model
lists never clobber. Compiles on 2021.3 floor; Python + adapter/catalog tests green.
Claude-Session: https://claude.ai/code/session_01GCxmdd4qo7MG6J4M6WcT9Y
Add the Ocean split-cube brand mark to the Editor UI and land three
low-risk installation-flow improvements.
Logo:
- OceanMark: reusable UI Toolkit control drawing the mark in Painter2D
vector (geometry transcribed from logo-mark.svg, brand colors baked in),
crisp at any DPI with no new package dependency. Painter2D is 2022.1+,
so it is guarded by #if UNITY_2022_1_OR_NEWER with a raster fallback
(package-icon.png) on the 2021.3 floor.
- Embedded in the main window header (#header-left group) and the
setup-wizard header.
- Ship package-icon.png inside the package (listing icon + 2021.3 fallback).
Installation polish:
- One-click uv install: UvInstaller builds the official installer command
per platform; setup wizard runs it off-thread with a confirm dialog and
re-checks dependencies.
- Claude CLI auto-discovery: consolidate the weaker resolver into
ExecPath.ResolveClaude() and add the ~/.claude/local (migrate-installer)
location both resolvers missed.
- Setup wizard clarity: success/next-step copy after configuring clients.
Tests: EditMode coverage for the geometry/color mapping, per-platform uv
command, and the Claude override contract.
Verified: compiles clean on 2021.3.45f2 (fallback path) and 6000.4.11f1
(Painter2D path).
Informational row (not a keyed provider) with a best-effort "Blender app detected"
status and a pointer to the blender-to-unity workflow / import_model_file. BlenderMCP
runs in the AI client and isn't detectable from Unity, so this reports only the
locally installed Blender app.
- BlenderDetection helper: checks well-known install paths + PATH per platform; the
pure DetectIn(candidates, exists) core is unit-tested with a fake predicate.
- McpAssetGenSection: AddBlenderHandoffRow() appended after the provider rows.
- README: note the tab's best-effort detection + that BlenderMCP lives in the AI client.
Verified: package compiles clean; EditMode 972/1018 pass, 0 failures (4 new
BlenderDetection tests) via tools/local_harness.py.
Claude-Session: https://claude.ai/code/session_015DAUrMR5UaSEzEn2wNPrEP
Security review + code review of the asset-gen feature surfaced concrete issues;
this fixes them and adds regression tests (request-shaping layer, FakeHttpTransport).
Security
- SafeZipExtractor enforces an extension allowlist; ModelImportPipeline passes an
inert model/texture allowlist so a provider archive can't drop a .cs/.dll under
Assets/ and have the Editor compile/load it (code execution on import).
- AssetGenJobManager refuses non-http(s) download URLs before fetching
(file:// SSRF / local-file read into the project).
Provider correctness
- Meshy image->3D polls /openapi/v1/image-to-3d/{id} (was the v2 text URL).
- Meshy text->3D honors texture=true via the preview->refine two-phase flow.
- OpenRouter image->image attaches the reference image (content image_url part).
- fal image->image uses the /edit endpoint + image_urls array; width/height
forwarded as image_size.
- Sketchfab search forwards categories/count/cursor/downloadable; preview doc
corrected (returns metadata, not a base64 thumbnail).
- Job import calls AssetDatabase.Refresh() before importing a freshly written file.
Local image input (image_path)
- New LocalImage helper; image_path is read and sent inline as a base64 data URI
for Meshy / fal / OpenRouter. Tripo rejects local images with a clear error
(needs a hosted image_url; its upload flow is not wired).
Cleanup (no behavior change)
- Shared AssetGenPaths + ProviderHttp helpers, HttpResult.Ok, MissingKeyMessage,
cached glTFast probe, dead-field / per-frame-alloc removal, CLI _emit.
Docs: README + manual-verification updated (image_path support; transparency is
import-flag-only; width/height fal-only).
Verified: package compiles clean; Python 1306 passed / 3 skipped. Meshy refine,
fal /edit, and image_path data-URI paths are unit-tested at the request layer
only -- live smoke per provider (real keys) still pending.
Claude-Session: https://claude.ai/code/session_015DAUrMR5UaSEzEn2wNPrEP
- Register 'asset_gen' tool group (off by default, parity with vfx/animation)
- EditorPrefKeys.AssetGen.* consts (non-secret config only; keys go to secure store)
- AssetGenPrefs helper (provider/format/output-root/normalize/enabled) + EditMode tests
- Python scaffold test asserts group present and disabled by default
Claude-Session: https://claude.ai/code/session_01Tjpb5gYgUe2AUJuRdXr7Lv
README-zh.md edits + .meta/uv.lock churn on brand-distribution-analytics, committed
(not stashed) so the feature branch can be checked out in the main worktree for editor
testing. Untracked .agents/ and AGENTS.md intentionally left out.
Claude-Session: https://claude.ai/code/session_01Tjpb5gYgUe2AUJuRdXr7Lv
Kilo Code v7.0.33+ moved MCP config out of the VS Code extension's
globalStorage/mcp_settings.json to a CLI-style kilo.jsonc under ~/.config/kilo,
with a new schema (https://app.kilo.ai/config.json): an "mcp" container,
type:"remote" for HTTP servers (type:"local" for stdio), and an "enabled" flag.
Writing the legacy mcpServers / type:"http" / disabled config left the server
showing as stdio + disabled.
- KiloCodeConfigurator targets ~/.config/kilo/kilo.jsonc on every OS and declares
the new format (mcp container, type:remote/local, enabled:true, $schema)
- Generalize the per-client HTTP "type" override: replace the UsesStreamableHttpType
bool with McpClient.HttpTypeValue (Cline/Roo => "streamableHttp", Kilo => "remote",
default "http"); add StdioTypeValue, ServerContainerKey and SchemaUrl fields
- ConfigJsonBuilder honors ServerContainerKey ("mcp") and writes a root $schema;
JsonFileMcpConfigurator.CheckStatus/Unregister read/remove from the configured
container so status detection and teardown work for Kilo
- Replace StreamableHttpTypeTests with ClientConfigFormatTests covering Kilo's
remote/mcp/enabled format, Cline's streamableHttp, and generic http
Verified: 5 new tests + 13 existing config tests pass (EditMode, Unity 2021.3).
After a domain reload the previous TcpListener's OS socket is not always
released by the time Start() rebinds, so listener.Start() throws
AddressAlreadyInUse. The old catch silently switched to a new port via
DiscoverNewPort(), which stranded the Python client (pinned to the configured
port) on the orphan listener and returned busy/timeout indefinitely.
Instead, keep the configured port and fail the start attempt WITHOUT blocking;
the reload handler's async resume schedule and the editor-idle retry re-invoke
Start() on the same port within ~1s, by which point the OS has released it.
Only after the port stays busy past a short window (PortManager.
ShouldAbandonBusyPort, 3s) is it treated as a foreign occupant and the bridge
falls back to a new port — loudly (McpLog.Warn) instead of the previous
debug-only switch.
This keeps Stop()'s 500ms wait intact (no #787 backgrounded-stall regression)
and is platform-agnostic (the race also reproduces on macOS, per #1173).
Also classify ObjectDisposedException from a torn-down NetworkStream as a benign
client disconnect so it no longer spams the Console as a red error (#1187).
UnityTypeResolver.FindCandidates treated a short name as ambiguous when an internal type shared it (richer assembly sets), so unqualified generics like List<T>/Dictionary<TKey,TValue> failed to resolve while fully-qualified names worked. Add DisambiguateByIdentity: de-dupe by FullName, then prefer a single public type, then a single core/BCL type, narrowing only to a unique survivor so genuine clashes still report ambiguous.
Fix EditMode tests that encoded built-in-pipeline/contract assumptions and only failed under URP + ProBuilder (the package test project uses the built-in pipeline and lacks ProBuilder, so these were never exercised):
- ManageGraphics negative tests read result["error"] (ErrorResponse has no "message" field); feature_add uses the handler's "type" param, not "feature_type"
- ManageMaterialProperties uses the pipeline-appropriate color property (_BaseColor on URP/HDRP)
- MCPToolParameterTests asserts pipeline-appropriate shader name and reads _Smoothness on URP/HDRP
- ManageProBuilder get_mesh_info test requests include:"faces"
Verified in TestbedMCP (URP 17.2 + ProBuilder 6.0.9): full EditMode suite 887 passed / 0 failed / 46 skipped.
1.Add Compat based scripts revolving around UnityCompatShims.cs, that will document our current API Compatibility changes in several files.
2.Add custom screenshot folder selection
Addresses review feedback on the Unity 6.5 GetInstanceID migration:
- UnityObjectIdCompatExtensions: drop the reflective method lookup in
favor of a simple #if UNITY_6000_5_OR_NEWER / #else split calling
GetEntityId() or GetInstanceID() directly. Also wrap the class in
the MCPForUnity.Runtime.Helpers namespace.
- UnityTypeConverters: remove the reflective EntityIdToObject probe and
call EditorUtility.EntityIdToObject(EntityId) directly under the same
version gate. Serialize entityID as EntityId.ToULong() rather than
ToString(), since Unity's docs explicitly warn that the textual form
is not a stable serialization contract.
- Drop #pragma warning disable 0619 from 22 files that no longer make
any direct calls to obsolete APIs. The remaining 7 files still need
it (FindObjectsOfType, InstanceIDToObject fallback) and are left as-is
— those deprecations are out of scope for this PR.
- Add the MCPForUnity.Runtime.Helpers using to every file that calls
GetInstanceIDCompat() now that the extension method lives in a
namespace.
Place the compatibility extension in MCPForUnity.Runtime so Runtime/Serialization can resolve GetInstanceIDCompat, fixing Unity 2022 compile errors from assembly visibility.
Replace direct GetInstanceID calls with a compatibility helper and update serialization to handle EntityId-era identifiers, so the package compiles on Unity 6.5 while preserving behavior on older versions.
ComponentOps: preserve the original objectReferenceValue before trying to assign found sprites; if Unity rejects the assignment (resulting in a null reference), restore the original value and return a clear error indicating the sprite name or fileID was incompatible with the property type.
ManageScriptableObject: use TryGetValue to read the legacy "ref" token so non-JObject tokens (e.g. string GUIDs) are preserved; treat ref or value as the resolve token and return a structured error when neither is present. These changes improve robustness when object references are incompatible or when legacy ref formats are used.
1. Add McpLog that could log mcp calls and errors under Asset/
2. Solve issues #816 via incluging str in annotation, extracted AssignedObjectreference() helper that verify assignments and resolve component types from gameobjects.
- unchecked((long)targetObjectId) prevents OverflowException for large ulong fileIDs
- Replace silent catch with McpLog.Warn including sprite name and instanceID
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace non-existent ParamCoercion.CoerceLong with fileIdToken.Value<long>()
- Add error return when fileID matches no sprite (consistent with spriteName branch)
- Remove extra blank line before GetSpriteFileId
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Initial update
* Update with docs and fix
* Update on Skybox
* Bug fix and compatability issue
* Update
* Resolve EntityIdToInstance and InstanceIdToObject conflict
* update to revert the changes
Seems EntityId is implicitly casted to Int so no need to prevent it.
* Initial update on tool list update
* clean up and doc update
* Update for Stdio mode
* UI change and doc update
* fix based on audit
* Update manage_tools.py
* Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1. Chinese Readme and Manifest order fix that might tackle Issue#847
2. Some fixes on Render and RenderMaterial, making sure the ParticleEffect material will use RP-according materials.
* Animation First PR
* Update ClipPresets to take account of local offset
* Update MCPForUnity/Editor/Tools/Animation/ClipCreate.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update for AI fix
* Temp Update
* update
* update
* update
* Initial update
* Delete 2026-02-09-implement-the-following-plan.txt
* update
* Remove scene generator, 3D gen, and unrelated files from PR
Remove files that don't belong in this camera/screenshot PR:
- Scene generator pipeline (Server/src/scene_generator/*)
- Manage3DGen tool (C# + Python)
- Generated test scripts (TestProjects/*/Scripts/*)
- Root-level docs and scripts (ProposedTable.md, system-prompt.md, start-scene-builder.*)
- Revert MCPForUnity.Editor.asmdef and CLAUDE.md to beta
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove ObjectTransformHistory from PR
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Update pyproject.toml
* Update MCPForUnity/Editor/Tools/GameObjects/GameObjectLookAt.cs
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
* Update Server/src/services/tools/manage_scene.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* update based on ai feedback
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
* Fix StdioBridgeHost zombie state after domain reload (#785)
After domain reload, the bridge could accept TCP connections but never
process commands. Three defensive fixes address the plausible failure
vectors:
1. Stale client cleanup: when a new client connects, close all other
active clients (in stdio there is only one server). Forces hung
ReadFrameAsUtf8Async to throw and exit cleanly.
2. ProcessCommands self-healing: track consecutive TCS timeouts. After
2+ consecutive, force re-register ProcessCommands on
EditorApplication.update and reset the reentrancy guard.
3. Stale command eviction: commands stuck with IsExecuting=true for
more than 60s are evicted from the queue with an error result.
Also adds always-on diagnostic logging at client connect/disconnect
with active client counts.
Includes E2E reconnection tests that verify both abrupt disconnect
recovery and stale client cleanup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix stdio bridge stalls when Unity is backgrounded during domain reload
- Add QueuePlayerLoopUpdate after command enqueue so ProcessCommands fires
even when Unity is backgrounded (mirrors HTTP RequestMainThreadPump)
- Keep ProcessCommands permanently registered on EditorApplication.update
to eliminate the registration gap between Stop and Start during reload
- Replace blocking retry loop in Start() (Thread.Sleep x 10) with single
attempt + port-switch fallback; async retries handled by reload handler
- Replace fire-and-forget TryStartBridgeImmediate with async retry loop
(6 attempts with backoff: 0s, 1s, 3s, 5s, 10s, 30s) matching HTTP
- Fix macOS port conflict: use ExclusiveAddressUse in both the real
listener (CreateConfiguredListener) and the port probe (IsPortAvailable)
to prevent AssetImportWorkers from binding the same port via SO_REUSEADDR
- Add PortManager unit tests (5 tests including macOS SO_REUSEADDR case)
Fixes#785
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add resource URI reference table to unity-mcp-skill
Adds a prominent 'Do NOT Guess' section with a URI lookup table near
the top of the skill, so LLMs use exact URIs instead of fabricating
similar-looking ones.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address PR review feedback
- Collapse double lock in HandleClientAsync (add + count in one lock)
- Add CancellationToken to ResumeStdioWithRetriesAsync so retries abort
on editor quit or subsequent domain reloads
- Fix 'gameobject' → 'GameObject' capitalization in skill URI table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address CodeRabbit review feedback
- PortManagerTests: save/restore on-disk port files in SetUp/TearDown
to prevent DiscoverNewPort from mutating persistent state
- StdioBridgeReconnectTests: replace blocking Connect with ConnectAsync
+ Wait(ConnectTimeoutMs) so CI tests cannot hang indefinitely
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Address CodeRabbit nitpicks: relocate static cache fields above the
methods that use them for readability, extract GetCachedOfflineProbeResult()
so GetUvxDevFlags()/GetUvxDevFlagsList() evaluate ShouldForceUvxRefresh()
only once per call instead of twice.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>