Skip to content

feat+fix: one-click client connection + autotest bug-fix batch#1142

Merged
Scriptwonder merged 24 commits into
CoplayDev:betafrom
Scriptwonder:fix/auto-test-multi
May 22, 2026
Merged

feat+fix: one-click client connection + autotest bug-fix batch#1142
Scriptwonder merged 24 commits into
CoplayDev:betafrom
Scriptwonder:fix/auto-test-multi

Conversation

@Scriptwonder

@Scriptwonder Scriptwonder commented May 22, 2026

Copy link
Copy Markdown
Collaborator

Two pieces of work stacked together.

The bigger one is a client-configuration overhaul that turns the per-client setup into an actual one-click flow. We have been getting a lot of asks and issues around the connection, which is indeed worth looking at. Each configurator now declares whether its target is installed on the machine, which transports it supports, and which one to coerce the user's preference toward. Configure All Detected Clients only writes for clients that actually exist. At Editor startup, every detected client gets a quiet CheckStatus pass and a silent rewrite if its config has drifted (transport mismatch, server version drift after a plugin update, etc.). The first-run wizard got a picker step so new users tick the clients they want and one button finishes the wiring. README + onboarding docs updated to match.

In the GUI, the prominent action is finally the prominent action. "Configure All Detected Clients" is now a bright green primary button at the top of the section instead of a low-contrast gray button at the bottom, and the per-client dropdown / status / Configure / path-overrides / manual-config row collapses behind a "Per-client setup" foldout (collapsed by default, state remembered between sessions).

Then a batch of bug fixes from a follow-up autotest pass.

#1138UnityEngineObjectConverter was declared public with a default constructor, so third-party Newtonsoft scanners (jillejr's converters) auto-discovered it and bound it into JsonConvert.DefaultSettings. Any unrelated project code that serialized a UnityEngine.Object reference suddenly got an asset path string back, just from having MCP for Unity installed. Made the converter internal and added a Runtime AssemblyInfo exposing internals to the Editor and test assemblies so the existing internal consumers (UnityJsonSerializer, GameObjectSerializer) still see it.

#1134CommandRegistry.AutoDiscoverCommands runs from [InitializeOnLoad], which also fires in the AssetImportWorker subprocess. Mono can hard-crash inside GetCustomAttribute<T>() on partially-loaded types in that subprocess and take the main Editor down through the IPC channel. The worker doesn't host MCP transport, so the scan is unnecessary there — bail out via AssetDatabase.IsAssetImportWorkerProcess() (looked up reflectively to tolerate Unity version drift, with a command-line fallback). Also wrapped each GetCustomAttribute<T>() in a try/catch helper so one bad type can't abort the whole scan.

#946execute_custom_tool's signature was parameters: dict | None. FastMCP generates a permissive schema from that which strict clients like Roo Code reject during tool discovery, so the tool just looked missing. Bumped to dict[str, Any] | None. The other four files cited in the issue were already parametrized in earlier commits — this was the last bare dict on an MCP-exposed boundary.

#865 — Multi-agent and sub-agent workflows churn through stdio connections, and three lifecycle messages fired on every reconnect: Client connected …, Closing N stale client(s) after new connection, Client handler exited …. The reporter saw 185+ lines in one session. All three moved behind McpLog.Info(..., always: false) so they only surface with verbose logging on. Startup messages and anomaly warnings stay at INFO.

Antigravity 2.0 / Windsurf / Gemini CLI HTTP detection (Discord report, and the one most relevant to the connection overhaul above). After a successful HTTP Configure, the configurator parsed back configuredUrl=null and reported IncorrectPath. That alone was annoying; combined with the new startup auto-rewrite, the Editor was silently rewriting those three clients' config files on every launch. ConfigJsonBuilder writes per client — serverUrl for Antigravity 2.0 and Windsurf, httpUrl for Gemini CLI, url for everyone else — but JsonFileMcpConfigurator.CheckStatus deserialized via a model that only knew url. Switched the read side to JObject parsing with url ?? serverUrl ?? httpUrl. EditMode tests lock all three property names against a synthetic configurator.

VFX Graph "not installed" when actually installed (Discord report, Unity 6 + VFX Graph 17.x). Two stacked bugs. First, every VFX path is gated behind #if UNITY_VFX_GRAPH, but MCPForUnity.Editor.asmdef had an empty versionDefines array, so that symbol was never defined for anyone — every action returned the "not installed" stub regardless of package presence. An older comment in the codebase actually said "Please enable the symbol in the project settings", expecting users to flip a scripting-define by hand. Added a proper versionDefines entry so Unity defines the symbol whenever com.unity.visualeffectgraph is installed at any version. Second, VfxGraphAssets.ValidateVfxGraphVersion() had a { "12.1" } allowlist — Unity 6's 14.x / 17.x would still have failed create_asset with "Unsupported VFX Graph version 17.x.x". Dropped the allowlist; kept the cheap runtime PackageInfo existence check as defense for the brief install/uninstall window. Verified end-to-end on VFX Graph 17.2.0.

Setup window dependency buttons stuck on "Installing…" (Discord report). AddDependencyRow flipped the button to "Installing…" and only restored it inside a synchronous try/catch. UPM Client.AddAndRemove is async, so on any failure (ETIMEDOUT to download.packages.unity.com in the reporter's case) the request eventually completed inside PollUpmRequest, logged the error, and stranded the button. The "Install All" path already threaded a completion callback — per-package didn't. Threaded an Action<Action> through installAction / uninstallAction so PollUpmRequest's completion (which fires for success and failure both) restores the button label.

Closes #865, #946, #1134, #1138.

Summary by CodeRabbit

  • New Features

    • Setup wizard now includes a client-configuration step and can configure selected installed clients.
    • Automatic re-check/rewriting of detected client configs on Editor startup.
    • Client-details foldout state persists across sessions.
  • Bug Fixes

    • Uninstalled clients are skipped when configuring all detected clients.
    • Transport selection is auto-coerced per-client (Claude Desktop uses stdio-only).
  • Style

    • Primary action button styling and placement updated in client UI.
  • Documentation

    • Quick-start and configurator docs updated to match new flow and transport behavior.
  • Tests

    • New EditMode tests covering install detection, supported transports, and startup rewrite.

Review Change Stack

Reflect the new onboarding wizard, multi-client one-click flow,
Claude Desktop transport auto-coercion, and startup config rewrite
in the README. Update the Claude Desktop section of the configurator
guide to drop the stale "throw on HTTP" language.
JsonFileMcpConfigurator.CheckStatus deserialized the standard
mcpServers layout through McpConfigServer.url, so HTTP configs
that use serverUrl (Antigravity 2.0, Windsurf) or httpUrl
(Gemini CLI) parsed with configuredUrl=null. The configurator
then reported transport=Unknown and status=IncorrectPath even
right after a successful Configure() that wrote a valid HTTP
entry, and the new startup auto-rewrite path kept rewriting
those files every Editor launch.

Switch the standard-layout branch to JObject parsing (same
shape as the VS Code branch) and accept url / serverUrl /
httpUrl. Add CheckStatus tests that lock all three property
names against a synthetic configurator.
…very (CoplayDev#1138)

The converter was declared public with a default constructor, so generic
Newtonsoft scanners (e.g. jillejr's newtonsoft-json-for-unity converters)
could pick it up and bind it into JsonConvert.DefaultSettings. Once
attached globally, any unrelated project code serializing a struct or
class containing a UnityEngine.Object reference would silently get an
asset path string back instead of the expected object JSON — purely from
having MCP for Unity installed.

Make the converter internal and expose Runtime internals to the Editor
and test assemblies via a Runtime AssemblyInfo so existing internal
consumers (UnityJsonSerializer, GameObjectSerializer) keep compiling.
…ction (CoplayDev#1134)

AssetImportWorker is a separate Editor subprocess that doesn't host the
MCP transport, so it has no reason to scan loaded assemblies. But our
[InitializeOnLoad] path runs there too, and Mono's reflection layer can
hard-crash inside type.GetCustomAttribute<T>() when assembly metadata
isn't fully restored after domain reload in the worker process — the
crash in the linked report freezes/kills the main Editor through the
import worker's IPC channel.

Detect the worker process via AssetDatabase.IsAssetImportWorkerProcess
(looked up reflectively so we tolerate visibility differences across
Unity versions) with a -importWorker command-line fallback, and bail
out of AutoDiscoverCommands before scanning. As a secondary belt, wrap
GetCustomAttribute<T>() in a try/catch helper so any single bad type
no longer aborts the entire registration pass.
CoplayDev#946)

FastMCP derives the wire schema from each tool's type annotations.
execute_custom_tool declared `parameters: dict | None`, which generates
a permissive schema some strict clients (Roo Code, certain VSCode MCP
adapters) reject during tool discovery — the tool then appears missing
even though everything else is healthy.

Switch to `dict[str, Any] | None` so the generated schema includes
proper additionalProperties bounds. The four other files cited in CoplayDev#946
(custom_tool_service, manage_components, manage_material, manage_texture)
were already parametrised in prior commits; execute_custom_tool was the
last remaining MCP-exposed tool with a bare `dict` annotation.
…ayDev#865)

Multi-agent and sub-agent workflows churn through stdio connections —
each new agent triggers a connect → close-stale → exit cycle, so the
three associated McpLog.Info calls in StdioBridgeHost flooded the Unity
console (the reporter saw 185+ lines in a single session). They're
useful for debugging a single hung connection but pure noise during
normal multi-agent operation.

Move all three to always:false so they only surface when verbose
logging is on, matching the existing recv-frame log on the same path.
Lifecycle messages that fire once (startup, port switch) and warnings
about anomalies (TCS timeout, queue eviction) are left at INFO.
Now that "Configure All Detected Clients" actually does what its name
says (auto-rewrite + per-client transport coercion + IsInstalled
filtering all landed in the recent client-config work), it's the path
we want first-run users on — not buried at the bottom of the panel.

Layout changes in McpClientConfigSection.uxml:
- Move the Configure-All button to the top of the section, right under
  the "Client Configuration" header, with a one-line helper underneath.
- Wrap the dropdown / status / single-client Configure button / Claude
  CLI path / project-dir / Manual Configuration foldout in a new
  "Configure a single client" foldout, collapsed by default. Persist
  its open/closed state via a new EditorPrefs key.

Style changes in Common.uss:
- New .primary-button class (bright green, 34px, bold) for the
  one-click action so it visually distinguishes itself from the regular
  blue .action-button rows.
- Light/dark-aware foldout header styling for the new client-details
  foldout that matches the existing manual-command-foldout treatment.
…t setup"

Helper line under the green button was visual noise — the button label
already says what it does. "Configure a single client" was redundant
inside a section titled "Client Configuration"; "Per-client setup"
reads cleaner. Also drop the now-orphan .primary-button-hint style.
…allowlist

The whole manage_vfx tool dispatcher and every VfxGraph* helper is
gated behind '#if UNITY_VFX_GRAPH', but nothing in the Editor asmdef
ever defined that symbol. Users with com.unity.visualeffectgraph
properly installed (e.g. Unity 6.3 + VFX Graph 17.x) hit the false
"VFX Graph package (com.unity.visualeffectgraph) not installed" branch
for every action, including read-only ones like list_templates and
get_info — reported via Discord.

Add a versionDefines entry to MCPForUnity.Editor.asmdef so Unity sets
UNITY_VFX_GRAPH whenever the VFX Graph package is present at any
version (`expression: 0.0.0`). This is the canonical way to detect
optional packages and lets the existing #if branches do their job.

Also drop ValidateVfxGraphVersion's hard-coded {"12.1"} allowlist,
which only matches Unity 2022.3-era VFX Graph and would still block
CreateAsset on modern installs even after the compile-gate fix. The
asset-level APIs we touch (VisualEffectAsset, AssetDatabase.CopyAsset,
template enumeration via PackageInfo) are stable across the
12.x → 17.x range, so the safer guard is just "package present" with
the compile-time gate handling the real "not installed" path.
…ailure

AddDependencyRow's click handlers flipped the button to "Installing..."
and only restored it inside a synchronous try/catch. But the per-package
call sites passed `() => InstallUpmPackage(...)` — no onComplete — so
when the UPM AddAndRemove request eventually completed (success OR
failure), nothing in PollUpmRequest's continuation ever told the button
to revert. A network timeout on com.unity.cinemachine therefore parked
the button on "Installing..." forever (Discord report); same for
Removing... on uninstall. The "Install All" path was fine because it
already threaded an onComplete callback through.

Change the install/uninstall hooks to Action<Action>, thread a `restore`
callback from the click handler into the UPM helpers, and invoke it
from PollUpmRequest's completion regardless of StatusCode. Roslyn (the
one synchronous install) just invokes the callback inline after Install
returns. The unrelated CRLF/LF noise in the diff is a side effect of
normalizing the file (988 CRLF lines, 41 LF lines pre-existing) onto
the dominant CRLF convention while my edits were in flight.
Copilot AI review requested due to automatic review settings May 22, 2026 11:11
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5d5fbf5e-5a7a-4aaf-8571-45050eb861c4

📥 Commits

Reviewing files that changed from the base of the PR and between abfc66a and b70babf.

📒 Files selected for processing (8)
  • MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
  • MCPForUnity/Editor/Services/ClientConfigurationService.cs
  • MCPForUnity/Editor/Services/StartupConfigRewrite.cs
  • MCPForUnity/Editor/Windows/MCPSetupWindow.cs
  • README.md
  • Server/src/services/tools/execute_custom_tool.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/ConfigureDetectedClientsTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/StartupConfigRewriteTests.cs

📝 Walkthrough

Walkthrough

This PR adds declarative configurator capabilities (IsInstalled, SupportedTransports), routes configuration through a transport-coercing wrapper, auto-rewrites detected client configs once per editor session, adds a setup-wizard clients step and UI persistence/styling, and includes robustness, visibility, and test additions.

Changes

Client Capability System & Automated Configuration

Layer / File(s) Summary
Configurator interface and base capability properties
MCPForUnity/Editor/Clients/IMcpClientConfigurator.cs, MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
IMcpClientConfigurator adds IsInstalled and SupportedTransports. McpClientConfiguratorBase provides defaults and ParentDirectoryExists helper.
Configurator implementations and JSON parsing
MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs, MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
Claude Desktop exposes stdio-only. JsonFile/Codex use parent-directory checks for installation. JsonFileMcpConfigurator.CheckStatus accepts url, serverUrl, httpUrl and unified JToken/JObject parsing. Claude CLI detects via MCPServiceLocator.Paths.
Transport-coercion configuration service
MCPForUnity/Editor/Services/ClientConfigurationService.cs
ConfigureClient/ConfigureAllDetectedClients now call ConfigureWithTransportCoercion which temporarily coerces UseHttpTransport to a supported transport; non-installed configurators are skipped with SkippedCount.
Startup config rewrite
MCPForUnity/Editor/Services/StartupConfigRewrite.cs
New [InitializeOnLoad] entrypoint runs once per session (skips batch/AssetImportWorker) and calls CheckStatus(attemptAutoRewrite: true) for installed clients, logging a summary of refreshed configs.
Setup wizard: clients step
MCPForUnity/Editor/Windows/MCPSetupWindow.cs, MCPForUnity/Editor/Windows/MCPSetupWindow.uxml
Adds clients step with toggles for installed clients, skip/configure-selected actions that call ConfigureClient, per-client success/failure accounting, and conditional Done-step advancement.
Client config UI and styles
MCPForUnity/Editor/Constants/EditorPrefKeys.cs, MCPForUnity/Editor/Windows/Components/ClientConfig/*, MCPForUnity/Editor/Windows/Components/Common.uss
Adds ClientDetailsFoldoutOpen EditorPref key. McpClientConfigSection persists foldout state and builds configure-all result headline including skipped counts. UXML moves configure-all button above foldout; USS adds .primary-button and .client-details-foldout.
Capability and integration tests
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/*, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/*
New tests validate IsInstalled property, SupportedTransports per configurator, CheckStatus URL-field handling, ConfigureAllDetectedClients summary counts, and StartupConfigRewrite reflection/attribute metadata.

Robustness and Infrastructure Improvements

Layer / File(s) Summary
CommandRegistry safety for AssetImportWorker
MCPForUnity/Editor/Tools/CommandRegistry.cs
Auto-discovery skips scanning in AssetImportWorker processes and uses HasAttributeSafe<T> to swallow attribute-reflection failures.
VFX Graph detection & asmdef
MCPForUnity/Editor/MCPForUnity.Editor.asmdef, MCPForUnity/Editor/Tools/Vfx/VfxGraphAssets.cs
asmdef adds UNITY_VFX_GRAPH versionDefine for com.unity.visualeffectgraph. ValidateVfxGraphVersion now only checks package presence and no longer enforces specific supported versions.
StdioBridgeHost logging tweaks
MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs
Three McpLog.Info calls now pass always: false to reduce logging noise for client connect/close/exit events.
Assembly visibility, converter visibility, server typing
MCPForUnity/Runtime/AssemblyInfo.cs, MCPForUnity/Runtime/Serialization/UnityTypeConverters.cs, Server/src/services/tools/execute_custom_tool.py
Added InternalsVisibleTo for editor/tests; UnityEngineObjectConverter changed from publicinternal; server function execute_custom_tool tightened to `dict[str, Any]
Dependency install/uninstall callback refactor
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs
Dependency row helpers now accept a restore callback; install/uninstall actions invoke or pass the callback and UI reflects in-progress state and restores on completion/errors.
Docs, manifests, and minor
.gitignore, README.md, docs/guides/MCP_CLIENT_CONFIGURATORS.md, Unity .meta files
.gitignore adds .worktrees/. README and configurator guide updated to document the setup wizard and Claude stdio-only behavior via SupportedTransports. Multiple Unity .meta files added for new scripts/tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • CoplayDev/unity-mcp#852 — Overlaps on Claude Desktop configurator/manual-snippet behavior; this PR replaces that override with SupportedTransports + coercion.
  • CoplayDev/unity-mcp#624 — Related UI changes (configure-all button placement and styling) overlap with this PR.
  • CoplayDev/unity-mcp#902 — Related transporter/configurator refactors; both touch transport selection plumbing.

Suggested labels

codex

Suggested reviewers

  • msanatan
  • dsarno

Poem

🐰 I nibble configs, declare each transport's song,
Stdio sings softly while HTTP moves along,
On startup I tidy the freshly found files,
The wizard hops forward and saves all the smiles,
A small rabbit cheers: configs now behave strong.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main changes: a client-connection feature (one-click) and bug fixes from an autotest pass.
Description check ✅ Passed The PR description provides comprehensive detail on both the main feature and all bug fixes, with clear issue references and technical explanations.
Linked Issues check ✅ Passed Issue #865 objective (reduce log noise during multi-agent reconnects) is addressed by moving lifecycle logs behind McpLog.Info(..., always: false). Other objectives mentioned in description (#946, #1134, #1138) are also covered.
Out of Scope Changes check ✅ Passed All changes align with stated objectives: configurator overhaul, one-click UI flow, bug fixes for #865/#946/#1134/#1138, and related improvements. No extraneous changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter

codecov-commenter commented May 22, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
Server/src/services/tools/execute_custom_tool.py 80.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR combines a client-configuration UX overhaul (one-click setup, startup drift correction, and updated docs) with a batch of targeted fixes from autotesting (converter visibility, AssetImportWorker safety, MCP schema typing, noisy logs, HTTP URL parsing, VFX Graph detection/version gating, and async dependency button restoration).

Changes:

  • Adds “configure detected clients” flows (wizard picker + prominent UI action) and a startup pass that rewrites drifted client configs.
  • Improves configurator capability modeling (installed detection + supported transports) and updates docs/README accordingly.
  • Fixes several regressions/compat issues (Newtonsoft converter visibility, AssetImportWorker crash, strict client schema typing, HTTP URL property parsing, VFX Graph symbol/version, async UPM UI restore, log verbosity).

Reviewed changes

Copilot reviewed 28 out of 35 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/StartupConfigRewriteTests.cs.meta Adds Unity meta for new edit-mode test.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/StartupConfigRewriteTests.cs Adds tests asserting StartupConfigRewrite exists and has expected guards/attributes.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/ConfigureDetectedClientsTests.cs.meta Adds Unity meta for new edit-mode test.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/ConfigureDetectedClientsTests.cs Adds tests around ConfigureAllDetectedClients summary accounting.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/SupportedTransportsTests.cs.meta Adds Unity meta for new edit-mode test.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/SupportedTransportsTests.cs Adds tests for SupportedTransports exposure and specific configurator transport support.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/IsInstalledTests.cs.meta Adds Unity meta for new edit-mode test.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/IsInstalledTests.cs Adds tests asserting IsInstalled exists and behaves based on config path parent dir.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/CheckStatusUrlPropertyTests.cs.meta Adds Unity meta for new edit-mode test.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/CheckStatusUrlPropertyTests.cs Adds regression tests for CheckStatus accepting url/serverUrl/httpUrl.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients.meta Adds Unity meta for new test folder.
Server/src/services/tools/execute_custom_tool.py Fixes MCP tool schema strictness by typing parameters as dict[str, Any] | None.
README.md Updates onboarding docs to reflect wizard + “configure detected clients” workflows and gotchas.
MCPForUnity/Runtime/Serialization/UnityTypeConverters.cs Makes UnityEngineObjectConverter internal to prevent unintended global Json.NET converter discovery.
MCPForUnity/Runtime/AssemblyInfo.cs.meta Adds Unity meta for new runtime AssemblyInfo.
MCPForUnity/Runtime/AssemblyInfo.cs Exposes runtime internals to editor and test assemblies via InternalsVisibleTo.
MCPForUnity/Editor/Windows/MCPSetupWindow.uxml Splits setup window into dependency step + new “select clients to configure” step.
MCPForUnity/Editor/Windows/MCPSetupWindow.cs Implements step navigation, detected-client list, and configure-selected behavior.
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs Fixes per-dependency install/uninstall buttons getting stuck by threading completion callbacks.
MCPForUnity/Editor/Windows/Components/Common.uss Adds styling for primary one-click button and per-client foldout.
MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.uxml Promotes “Configure All Detected Clients” and collapses per-client controls into a foldout.
MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs Persists foldout state and adjusts configure-all dialog messaging.
MCPForUnity/Editor/Tools/Vfx/VfxGraphAssets.cs Removes restrictive VFX Graph version allowlist; retains package presence check.
MCPForUnity/Editor/Tools/CommandRegistry.cs Skips reflection scan in AssetImportWorker and makes attribute probing resilient to half-loaded types.
MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs Reduces reconnect log spam by demoting lifecycle logs behind non-verbose mode.
MCPForUnity/Editor/Services/StartupConfigRewrite.cs.meta Adds Unity meta for new startup rewrite service.
MCPForUnity/Editor/Services/StartupConfigRewrite.cs Adds startup-time drift correction by re-checking installed clients once per session.
MCPForUnity/Editor/Services/ClientConfigurationService.cs Adds “installed-only” configure-all and transport coercion when a client can’t support the global preference.
MCPForUnity/Editor/MCPForUnity.Editor.asmdef Defines UNITY_VFX_GRAPH automatically when com.unity.visualeffectgraph is installed.
MCPForUnity/Editor/Constants/EditorPrefKeys.cs Adds EditorPref key for per-client foldout persisted state.
MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs Adds IsInstalled + SupportedTransports, plus URL-property parsing improvements in JsonFile CheckStatus.
MCPForUnity/Editor/Clients/IMcpClientConfigurator.cs Extends configurator interface to include IsInstalled and SupportedTransports.
MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs Declares Claude Desktop as stdio-only via SupportedTransports instead of throwing on HTTP.
docs/guides/MCP_CLIENT_CONFIGURATORS.md Updates configurator docs to describe SupportedTransports + coercion flow.
.gitignore Ignores local .worktrees/ directory.
Files not reviewed (6)
  • MCPForUnity/Editor/Services/StartupConfigRewrite.cs.meta: Language not supported
  • MCPForUnity/Runtime/AssemblyInfo.cs.meta: Language not supported
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients.meta: Language not supported
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/CheckStatusUrlPropertyTests.cs.meta: Language not supported
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/IsInstalledTests.cs.meta: Language not supported
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/SupportedTransportsTests.cs.meta: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +96 to +108
bool currentlyHttp = EditorConfigurationCache.Instance.UseHttpTransport;
var requested = currentlyHttp ? ConfiguredTransport.Http : ConfiguredTransport.Stdio;

if (supported.Contains(requested)) return; // user preference is supported, no change

var chosen = supported[0];
bool needHttp = chosen == ConfiguredTransport.Http;
if (EditorConfigurationCache.Instance.UseHttpTransport != needHttp)
{
EditorConfigurationCache.Instance.SetUseHttpTransport(needHttp);
McpLog.Info(
$"[{configurator.DisplayName}] auto-selected {chosen} transport (client does not support {requested}).");
}
Comment on lines 30 to 37
public McpStatus Status => client.status;
public ConfiguredTransport ConfiguredTransport => client.configuredTransport;
public virtual bool SupportsAutoConfigure => true;
public virtual bool IsInstalled => true;
private static readonly ConfiguredTransport[] DefaultTransports =
{ ConfiguredTransport.Stdio, ConfiguredTransport.Http };
public virtual IReadOnlyList<ConfiguredTransport> SupportedTransports => DefaultTransports;
public virtual bool SupportsSkills => false;
Comment on lines +22 to +26
if (UnityEditorInternal.InternalEditorUtility.inBatchMode) return;
if (SessionState.GetBool(SESSION_GUARD_KEY, false)) return;
EditorApplication.delayCall += RunOnce;
}

Comment on lines +11 to +17
public void Summary_ContainsOnlyInstalledClients()
{
var svc = new ClientConfigurationService();
var summary = svc.ConfigureAllDetectedClients();
int installedCount = svc.GetAllClients().Count(c => c.IsInstalled);
Assert.AreEqual(installedCount, summary.SuccessCount + summary.FailureCount,
"Only installed clients should appear in success/failure totals");

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Server/src/services/tools/execute_custom_tool.py (1)

26-45: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

parameters=None is rejected despite the optional signature.

Line 26 allows None, but Line 41 rejects it, so calls that omit parameters fail with "parameters must be an object/dictionary". This breaks no-arg custom tools.

💡 Proposed fix
 async def execute_custom_tool(ctx: Context, tool_name: str, parameters: dict[str, Any] | None = None) -> MCPResponse:
@@
-    if not isinstance(parameters, dict):
+    if parameters is None:
+        parameters = {}
+    elif not isinstance(parameters, dict):
         return MCPResponse(
             success=False,
             message="parameters must be an object/dictionary",
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Server/src/services/tools/execute_custom_tool.py` around lines 26 - 45, The
function execute_custom_tool rejects parameters set to None even though its
signature allows it; update the logic in execute_custom_tool to accept
parameters=None by either treating None as an empty dict (e.g., assign
parameters = {} when parameters is None) or by changing the validation so None
is allowed and only non-None values must be a dict, referencing
execute_custom_tool and the existing isinstance(parameters, dict) check to
locate where to adjust the behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@MCPForUnity/Editor/Services/ClientConfigurationService.cs`:
- Around line 101-103: The code currently picks the first supported transport
(var chosen = supported[0]) which ignores the configurator's preferred
transport; change the selection to prefer the configurator-declared transport
(e.g., EditorConfigurationCache.Instance.ConfiguredTransport or similar
preferred symbol) if it is present in the supported list, and only fall back
deterministically to supported[0] when the preferred transport isn't available,
then compute needHttp from that chosen transport and compare to
EditorConfigurationCache.Instance.UseHttpTransport as before; update references
to chosen and needHttp in ClientConfigurationService (the block that sets
chosen, needHttp, and checks UseHttpTransport) accordingly.

In `@MCPForUnity/Editor/Services/StartupConfigRewrite.cs`:
- Around line 40-43: The current code reads c.Status into before and skips if
before == McpStatus.NotConfigured which can be stale; instead, call
c.CheckStatus(attemptAutoRewrite: true) first to get an up-to-date status and
then compare the previous in-memory status if needed. Remove the early continue
on c.Status, call var after = c.CheckStatus(attemptAutoRewrite: true)
immediately, and then use the before/after comparison (keeping before = c.Status
if you still want to detect transitions to McpStatus.Configured and increment
rewrote) so auto-rewrite runs for clients whose on-disk state is configured.

In `@MCPForUnity/Editor/Windows/MCPSetupWindow.cs`:
- Around line 163-187: In OnConfigureSelectedClicked, detect when no client
toggles are selected (i.e., iterate clientToggles or check a count of toggles
with toggle.value true) and if zero show an EditorUtility.DisplayDialog
prompting the user to select at least one client and then return early; ensure
you do not call Setup.SetupWindowService.MarkSetupCompleted() or Close() in that
case so the setup window stays open; otherwise proceed with the existing
configure loop, message aggregation, and final dialog as before.

In `@README.md`:
- Around line 90-93: The README contains conflicting guidance about Claude
Desktop's transport: update the manual config section to match the earlier
statement that "Claude Desktop only supports stdio." Specifically, edit any
lines that claim Claude Desktop can use HTTP so they instead say it is
stdio-only, and add a clarifying note that MCP for Unity will force stdio for
Claude Desktop even if HTTP is selected; ensure references to "MCP for Unity",
"Claude Desktop", "HTTP", and "stdio" reflect this single consistent behavior.

In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/ConfigureDetectedClientsTests.cs`:
- Around line 13-25: The test calls the concrete
ClientConfigurationService.ConfigureAllDetectedClients() which performs real,
side-effectful writes and makes the test machine-dependent; modify the test to
use a non-mutating substitute by injecting a fake/config-only configurator or
configuring ClientConfigurationService to use a temporary/dry-run config path
before calling ConfigureAllDetectedClients(): e.g., construct
ClientConfigurationService with a test-only IClientConfigurator mock or set its
config directory/property to a temp folder, then call
ConfigureAllDetectedClients() and assert counts against svc.GetAllClients() so
no real detected client files are written.

In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/StartupConfigRewriteTests.cs`:
- Around line 13-15: The test currently only checks that System.Type.GetType
returned a non-null Type (variable t) but does not verify public visibility;
update the assertion after obtaining t (for the type
"MCPForUnity.Editor.Services.StartupConfigRewrite") to explicitly assert its
visibility (e.g., assert t.IsVisible or Assert.IsTrue(t.IsPublic ||
t.IsNestedPublic) or Assert.IsTrue(t.IsVisible) with a clear message) so the
test truly validates the type is publicly visible.

---

Outside diff comments:
In `@Server/src/services/tools/execute_custom_tool.py`:
- Around line 26-45: The function execute_custom_tool rejects parameters set to
None even though its signature allows it; update the logic in
execute_custom_tool to accept parameters=None by either treating None as an
empty dict (e.g., assign parameters = {} when parameters is None) or by changing
the validation so None is allowed and only non-None values must be a dict,
referencing execute_custom_tool and the existing isinstance(parameters, dict)
check to locate where to adjust the behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0e7d16c6-9a10-419d-890c-1317c007cf9b

📥 Commits

Reviewing files that changed from the base of the PR and between 2999427 and abfc66a.

📒 Files selected for processing (35)
  • .gitignore
  • MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs
  • MCPForUnity/Editor/Clients/IMcpClientConfigurator.cs
  • MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
  • MCPForUnity/Editor/Constants/EditorPrefKeys.cs
  • MCPForUnity/Editor/MCPForUnity.Editor.asmdef
  • MCPForUnity/Editor/Services/ClientConfigurationService.cs
  • MCPForUnity/Editor/Services/StartupConfigRewrite.cs
  • MCPForUnity/Editor/Services/StartupConfigRewrite.cs.meta
  • MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs
  • MCPForUnity/Editor/Tools/CommandRegistry.cs
  • MCPForUnity/Editor/Tools/Vfx/VfxGraphAssets.cs
  • MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs
  • MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.uxml
  • MCPForUnity/Editor/Windows/Components/Common.uss
  • MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs
  • MCPForUnity/Editor/Windows/MCPSetupWindow.cs
  • MCPForUnity/Editor/Windows/MCPSetupWindow.uxml
  • MCPForUnity/Runtime/AssemblyInfo.cs
  • MCPForUnity/Runtime/AssemblyInfo.cs.meta
  • MCPForUnity/Runtime/Serialization/UnityTypeConverters.cs
  • README.md
  • Server/src/services/tools/execute_custom_tool.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/CheckStatusUrlPropertyTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/CheckStatusUrlPropertyTests.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/IsInstalledTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/IsInstalledTests.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/SupportedTransportsTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Clients/SupportedTransportsTests.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/ConfigureDetectedClientsTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/ConfigureDetectedClientsTests.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/StartupConfigRewriteTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/StartupConfigRewriteTests.cs.meta
  • docs/guides/MCP_CLIENT_CONFIGURATORS.md

Comment thread MCPForUnity/Editor/Services/ClientConfigurationService.cs Outdated
Comment thread MCPForUnity/Editor/Services/StartupConfigRewrite.cs
Comment thread MCPForUnity/Editor/Windows/MCPSetupWindow.cs
Comment thread README.md
Comment on lines +13 to +25
var svc = new ClientConfigurationService();
var summary = svc.ConfigureAllDetectedClients();
int installedCount = svc.GetAllClients().Count(c => c.IsInstalled);
Assert.AreEqual(installedCount, summary.SuccessCount + summary.FailureCount,
"Only installed clients should appear in success/failure totals");
}

[Test]
public void Summary_SkippedCountTracksUninstalled()
{
var svc = new ClientConfigurationService();
var summary = svc.ConfigureAllDetectedClients();
int uninstalledCount = svc.GetAllClients().Count(c => !c.IsInstalled);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid side-effectful config writes in EditMode unit tests.

Line 14 and Line 24 invoke ConfigureAllDetectedClients() on the concrete service, which can write real detected client configs and make this suite machine-dependent. Please isolate this with fake configurators/temp-only config paths (or a dry-run code path) so these assertions stay deterministic and non-mutating.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/ConfigureDetectedClientsTests.cs`
around lines 13 - 25, The test calls the concrete
ClientConfigurationService.ConfigureAllDetectedClients() which performs real,
side-effectful writes and makes the test machine-dependent; modify the test to
use a non-mutating substitute by injecting a fake/config-only configurator or
configuring ClientConfigurationService to use a temporary/dry-run config path
before calling ConfigureAllDetectedClients(): e.g., construct
ClientConfigurationService with a test-only IClientConfigurator mock or set its
config directory/property to a temp folder, then call
ConfigureAllDetectedClients() and assert counts against svc.GetAllClients() so
no real detected client files are written.

…fety (PR review)

Two correctness bugs flagged on PR CoplayDev#1142 (Copilot + CodeRabbit).

CoerceTransportFor only treated `ConfiguredTransport.Http` as an HTTP
choice, so a client that declares `SupportedTransports = { HttpRemote }`
would get flipped to stdio whenever the user preferred HTTP. The
fallback selection was also order-dependent (`supported[0]`), which
could pick stdio over Http when the user wanted HTTP and the client
supported both. Now: any HTTP variant satisfies an HTTP request, and
when we must fall back we prefer the variant that matches user intent
(HTTP family vs stdio).

StartupConfigRewrite shared two problems with the CommandRegistry path
from CoplayDev#1134: it fired in the AssetImportWorker subprocess too (where
half-loaded domains can crash Mono on reflection and where writing
client configs from a worker is plain wrong), and it skipped rewrite
when in-memory Status was NotConfigured *before* refreshing from disk
— on a fresh editor load that miscategorized clients that were already
configured on disk and prevented the auto-rewrite. Added the same
AssetImportWorker guard as CoplayDev#1134 (reflective lookup + cmdline fallback)
and dropped the pre-refresh status skip so CheckStatus always reads
the file before deciding.
…custom_tool (PR review)

Two review nits from PR CoplayDev#1142.

McpClientConfiguratorBase.IsInstalled defaulted to `true`, which meant
any future configurator that derives directly from the base (without
going through JsonFile/Codex/ClaudeCli) would be treated as "detected"
by ConfigureAllDetectedClients and could end up writing config files
for apps that aren't on the machine. Default to a cheap filesystem
check via ParentDirectoryExists(GetConfigPath()); the three existing
base classes that override with the same check are harmlessly redundant
now, and CLI configurators (where GetConfigPath isn't a real path) keep
their own overrides.

execute_custom_tool declared `parameters: dict[str, Any] | None = None`
in its signature but then rejected `None` at runtime with
"parameters must be an object/dictionary". For parameter-less custom
tools the type hint and the behavior contradicted each other. Coerce
`None` to an empty dict; reject only genuinely-wrong types.
…ish (PR review)

Four smaller items flagged on PR CoplayDev#1142.

MCPSetupWindow.OnConfigureSelectedClicked marked setup as completed
and closed the window even when the user hadn't ticked any client —
clicking "Configure Selected" with everything unchecked would silently
skip setup forever. Show a prompt and return early instead.

README listed Claude Desktop under the HTTP-default group while a
neighboring paragraph correctly says Claude Desktop is stdio-only;
removed it from the HTTP list and pointed at the stdio block.

StartupConfigRewrite_TypeExists only checked that the type resolved,
not that it's public — added an explicit IsPublic assertion so the
test matches its own message about the [InitializeOnLoad] requirement.

ConfigureDetectedClientsTests calls the real ClientConfigurationService
which walks McpClientRegistry and Configure()s every detected client —
on CI this is harmless (no clients installed) but on a dev machine it
mutates real user config files. Marked both tests [Explicit] until we
DI the configurator list through the service.
@Scriptwonder Scriptwonder merged commit c1df6c7 into CoplayDev:beta May 22, 2026
4 checks passed
@Scriptwonder Scriptwonder deleted the fix/auto-test-multi branch May 22, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Frequent server log

3 participants