Skip to content

Comments

Bug fix and batch customization#727

Merged
Scriptwonder merged 3 commits intoCoplayDev:betafrom
Scriptwonder:bug-fix-and-batch-customization
Feb 12, 2026
Merged

Bug fix and batch customization#727
Scriptwonder merged 3 commits intoCoplayDev:betafrom
Scriptwonder:bug-fix-and-batch-customization

Conversation

@Scriptwonder
Copy link
Collaborator

@Scriptwonder Scriptwonder commented Feb 11, 2026

Description

  1. Fix the bug that GitUrlOverride might get set to unity-mcp instead of unity-mcp/Server
  2. Make batch request customizable to up to 100 requests

Type of Change

Save your change type

  • Bug fix (non-breaking change that fixes an issue)
  • Refactoring (no functional changes)

Changes Made

Testing/Screenshots/Recordings

截屏2026-02-11 下午6 50 25 截屏2026-02-11 下午6 51 09

Documentation Updates

  • I have added/removed/modified tools or resources
  • If yes, I have updated all documentation files using:
    • The LLM prompt at tools/UPDATE_DOCS_PROMPT.md (recommended)
    • Manual updates following the guide at tools/UPDATE_DOCS.md

Related Issues

Additional Notes

Summary by Sourcery

Fix MCP server path overrides and make batch execution limits configurable while documenting UI creation workflows.

New Features:

  • Allow configuring the maximum commands per batch_execute call from the Unity MCP Tools window, up to a hard limit of 100.
  • Expose the configured batch_execute limit to the server via editor state so the backend enforces the same per-batch cap.
  • Add documented UI creation workflows demonstrating batch_execute patterns for common Unity UI elements and layouts.

Bug Fixes:

  • Automatically correct local MCP server override paths that point to the repo root to instead target the Server subdirectory when it contains pyproject.toml, both when reading and when browsing for the path.

Enhancements:

  • Clamp batch_execute command counts on both the Unity editor and server sides and improve error messages to reference the configurable limit and hard ceiling.
  • Extend editor state to include user settings such as the batch_execute max commands value.

Documentation:

  • Document configurable batch_execute limits and the hard maximum in skill docs.
  • Add detailed UI creation workflow documentation covering Canvas, EventSystem, Panel, Text, Button, Slider, Toggle, Input Field, layout groups, and a complete main menu example.

Summary by CodeRabbit

  • New Features

    • Batch operation limit is now configurable from the Tools window (default 25, hard max 100); server respects this setting.
    • Local server folder selection auto-corrects to the proper Server/pyproject.toml location when applicable.
  • Documentation

    • Updated skill and workflow docs to reflect the configurable batch limit and adjusted example wording.

1. Fix the bug where for certain MacOS user (like me), the GitURLoverride is set to the unity-mcp folder rather than the unity-mcp/Server
2. Customize batch for it to take 0-100 requests. Tested on MacOS.
Copilot AI review requested due to automatic review settings February 11, 2026 23:52
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 11, 2026

Reviewer's Guide

Makes batch_execute’s max-commands limit configurable (up to 100) and exposes it to both Unity and server-side logic, adds extensive UI creation batch templates to the skill docs, and fixes GitUrlOverride so local unity-mcp paths are auto-corrected to the Server subdirectory when needed.

Sequence diagram for configurable batch_execute limit between Unity and server

sequenceDiagram
    actor LlmUser
    participant ClaudeSkill
    participant ServerBatchExecute as Server_batch_execute_py
    participant ServerEditorState as Server_editor_state_service
    participant UnityEditor
    participant EditorStateCache
    participant BatchExecuteCs as BatchExecute_CSharp
    participant EditorPrefs

    LlmUser->>ClaudeSkill: Call batch_execute with commands
    ClaudeSkill->>ServerBatchExecute: batch_execute(ctx, commands, fail_fast)

    ServerBatchExecute->>ServerBatchExecute: Validate commands list not empty

    ServerBatchExecute->>ServerEditorState: _get_max_commands_from_editor_state(ctx)
    ServerEditorState->>UnityEditor: HTTP request for editor state
    UnityEditor->>EditorStateCache: BuildSnapshot(reason)
    EditorStateCache->>BatchExecuteCs: GetMaxCommandsPerBatch()
    BatchExecuteCs->>EditorPrefs: GetInt(BatchExecuteMaxCommands, DefaultMaxCommandsPerBatch)
    EditorPrefs-->>BatchExecuteCs: configured_or_default
    BatchExecuteCs-->>EditorStateCache: clamped_max_commands

    EditorStateCache-->>UnityEditor: EditorStateSnapshot with Settings.BatchExecuteMaxCommands
    UnityEditor-->>ServerEditorState: Serialized editor state response
    ServerEditorState-->>ServerBatchExecute: EditorStateData with settings.batch_execute_max_commands
    ServerBatchExecute->>ServerBatchExecute: Cache max_commands

    ServerBatchExecute->>ServerBatchExecute: Compare len(commands) to max_commands
    alt Too_many_commands
        ServerBatchExecute-->>ClaudeSkill: Error value: exceeds max_commands
        ClaudeSkill-->>LlmUser: Report batch_execute limit error
    else Within_limit
        ServerBatchExecute->>ServerBatchExecute: Normalize commands
        ServerBatchExecute->>ServerBatchExecute: Execute commands via Unity transport
        ServerBatchExecute-->>ClaudeSkill: Aggregated tool results
        ClaudeSkill-->>LlmUser: Return batch results
    end
Loading

Updated class diagram for batch_execute configuration and editor state models

classDiagram
    class EditorPrefKeys {
        <<static>>
        +string GitUrlOverride
        +string BatchExecuteMaxCommands
    }

    class BatchExecute_CSharp {
        <<static>>
        +int DefaultMaxCommandsPerBatch
        +int AbsoluteMaxCommandsPerBatch
        +int GetMaxCommandsPerBatch()
        +Task~object~ HandleCommand(JObject params)
    }

    class EditorStateCache {
        <<static>>
        +JObject BuildSnapshot(string reason)
    }

    class EditorStateSnapshot {
        +EditorStateTransport Transport
        +EditorStateSettings Settings
    }

    class EditorStateSettings {
        +int BatchExecuteMaxCommands
    }

    class EditorStateTransport {
        +bool? UnityBridgeConnected
        +long? LastMessageUnixMs
    }

    class EditorStateSettingsPy {
        +int~nullable~ batch_execute_max_commands
    }

    class EditorStateDataPy {
        +EditorStateSettingsPy~nullable~ settings
        +EditorStateTransportPy~nullable~ transport
    }

    class EditorStateTransportPy {
        +bool~nullable~ unity_bridge_connected
        +int~nullable~ last_message_unix_ms
    }

    class BatchExecute_Py {
        +int DEFAULT_MAX_COMMANDS_PER_BATCH
        +int ABSOLUTE_MAX_COMMANDS_PER_BATCH
        +int~nullable~ _cached_max_commands
        +async int _get_max_commands_from_editor_state(Context ctx)
        +void invalidate_cached_max_commands()
        +async Any batch_execute(Context ctx, list commands, bool fail_fast)
    }

    EditorStateCache --> EditorStateSnapshot
    EditorStateSnapshot --> EditorStateTransport
    EditorStateSnapshot --> EditorStateSettings

    EditorStateDataPy --> EditorStateSettingsPy
    EditorStateDataPy --> EditorStateTransportPy

    BatchExecute_CSharp ..> EditorPrefKeys : uses
    EditorStateCache ..> BatchExecute_CSharp : calls GetMaxCommandsPerBatch

    BatchExecute_Py ..> EditorStateDataPy : reads settings.batch_execute_max_commands
    BatchExecute_Py ..> BatchExecute_CSharp : shares limits via editor state

    EditorPrefKeys <.. McpToolsSection : BatchExecuteMaxCommands key used
    class McpToolsSection {
        +VisualElement CreateToolRow(ToolMetadata tool)
        -VisualElement CreateBatchExecuteSettings()
        -bool IsBatchExecuteTool(ToolMetadata tool)
    }
Loading

Flow diagram for auto-correcting local MCP server path overrides

flowchart TD
    Start[[Start]] --> CheckEmpty{Path empty?}
    CheckEmpty -->|Yes| ReturnOriginal1[Return original path]
    CheckEmpty -->|No| CheckRemote{Starts with http, https, git+, ssh?}

    CheckRemote -->|Yes| ReturnOriginal2[Return original path]
    CheckRemote -->|No| StripFilePrefix{Starts with file://?}

    StripFilePrefix -->|Yes| SetPrefix[Store prefix and strip from path]
    StripFilePrefix -->|No| NoPrefix[Use path as checkPath; prefix empty]

    SetPrefix --> CheckPyproject
    NoPrefix --> CheckPyproject

    CheckPyproject{pyproject.toml exists in checkPath?} -->|Yes| ReturnOriginal3[Return original path]
    CheckPyproject -->|No| CheckServerSubdir

    CheckServerSubdir{pyproject.toml exists in checkPath/Server?} -->|Yes| BuildCorrected[Return prefix + checkPath/Server]
    CheckServerSubdir -->|No| ReturnOriginal4[Return original path]

    ReturnOriginal1 --> End[[End]]
    ReturnOriginal2 --> End
    ReturnOriginal3 --> End
    ReturnOriginal4 --> End
    BuildCorrected --> End
Loading

File-Level Changes

Change Details Files
Auto-correct local MCP server override paths so they point at the Python Server directory containing pyproject.toml, and surface this behavior in the advanced settings UI.
  • GetMcpServerPackageSource now normalizes local overrides via a new ResolveLocalServerPath helper that looks for pyproject.toml in the chosen directory or its Server subfolder, preserving file:// prefixes and leaving non-local URLs/PyPI refs untouched.
  • When the resolved path differs from the stored override, the corrected value is written back to EditorPrefs and a log message is emitted for traceability.
  • The advanced MCP settings UI now resolves and normalizes the Git URL override on change and when using the folder picker, updating the text field without re-triggering callbacks and improving the folder dialog hint text.
  • A private ResolveServerPath helper in the advanced section mirrors the server-path resolution logic used in AssetPathUtility to keep editor UX consistent.
MCPForUnity/Editor/Helpers/AssetPathUtility.cs
MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs
Make batch_execute’s per-call command limit configurable from the Unity editor (with a hard cap) and keep the Python tool in sync via editor_state.
  • Introduced DefaultMaxCommandsPerBatch and AbsoluteMaxCommandsPerBatch constants plus a GetMaxCommandsPerBatch helper in the C# BatchExecute tool that reads and clamps a new EditorPrefs key.
  • Updated the C# BatchExecute handler to enforce the dynamic maxCommands value and return an error message that mentions configurability and the hard max.
  • Extended EditorPrefKeys with a BatchExecuteMaxCommands key, and added a new EditorStateSettings section to EditorStateCache and the serialized snapshot so the current limit is sent to the server.
  • On the server, replaced the fixed MAX_COMMANDS_PER_BATCH with async lookup of the configured limit from editor_state, including a small cache and invalidation function, and enforced this value in the batch_execute validator with updated error messaging.
  • Added a dedicated UI control in the MCP Tools window that only appears for batch_execute, allowing users to set the max commands per batch via an IntegerField with clamping and tooltip.
  • Updated SKILL docs to describe the new default and hard max limits for batch_execute and to mention the configuration UI in the MCP Tools window.
MCPForUnity/Editor/Tools/BatchExecute.cs
MCPForUnity/Editor/Constants/EditorPrefKeys.cs
MCPForUnity/Editor/Services/EditorStateCache.cs
Server/src/services/resources/editor_state.py
Server/src/services/tools/batch_execute.py
MCPForUnity/Editor/Windows/Components/Tools/McpToolsSection.cs
.claude/skills/unity-mcp-skill/SKILL.md
unity-mcp-skill/SKILL.md
Add detailed UI creation workflows and examples that leverage batch_execute for building common Unity UI hierarchies.
  • Inserted a new UI Creation Workflows section into both skill workflow reference docs, covering Canvas, EventSystem, Panel, TextMeshPro text, Button, Slider, TMP input field, Toggle, layout groups, and a complete main-menu example using batch_execute.
  • Documented enum/int mappings for TextMeshPro alignment, layout group childAlignment, and ContentSizeFitter modes to make property setting via tools less error-prone.
  • Extended the UI component quick-reference table to list common UI elements, their required components, and notes, explicitly tying these patterns back to batch_execute and giving users direct links from the SKILL overview to the new section.
.claude/skills/unity-mcp-skill/references/workflows.md
unity-mcp-skill/references/workflows.md
.claude/skills/unity-mcp-skill/SKILL.md
unity-mcp-skill/SKILL.md

Possibly linked issues

  • #(un-numbered): PR extends the Claude Unity MCP skill docs (batch_execute, workflows, schema guidance), directly addressing the requested skill additions.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This PR makes the batch_execute per-batch command limit configurable from the Unity editor (default 25, hard cap 100). Changes touch docs, editor prefs/UI, editor state snapshots, local server path resolution, and server-side batch_execute validation and caching.

Changes

Cohort / File(s) Summary
Documentation
\.claude/skills/unity-mcp-skill/SKILL.md, unity-mcp-skill/SKILL.md, \.claude/skills/unity-mcp-skill/references/workflows.md, unity-mcp-skill/references/workflows.md
Updated wording to state batch_execute uses a default of 25 commands per batch and is configurable in the Unity MCP Tools window up to a 100-command hard max.
Editor Prefs & Constants
MCPForUnity/Editor/Constants/EditorPrefKeys.cs
Added BatchExecuteMaxCommands EditorPref key.
Editor State Snapshot
MCPForUnity/Editor/Services/EditorStateCache.cs
Added EditorStateSettings with BatchExecuteMaxCommands and surfaced it on snapshot JSON as settings.
Batch Execute Editor Tooling
MCPForUnity/Editor/Tools/BatchExecute.cs, MCPForUnity/Editor/Windows/Components/Tools/McpToolsSection.cs
Replaced fixed max with DefaultMaxCommandsPerBatch (25) and AbsoluteMaxCommandsPerBatch (100); added GetMaxCommandsPerBatch(); added UI widget to configure max commands (clamped 1..100) and persist to EditorPrefs.
Advanced Editor Path Handling
MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs, MCPForUnity/Editor/Helpers/AssetPathUtility.cs
Added ResolveServerPath/ResolveLocalServerPath logic to auto-correct local server paths (detect Server/pyproject.toml) and persist corrected paths to EditorPrefs; integrated into Git URL and package source flows.
Server: Editor State & Batch Tooling
Server/src/services/resources/editor_state.py, Server/src/services/tools/batch_execute.py
Added EditorStateSettings and settings field on editor state model; server batch_execute reads cached editor-state-backed max (default/hard limits added), validates against configured max, and exposes invalidate/cache helpers.

Sequence Diagram(s)

sequenceDiagram
    participant Editor as Unity Editor (UI/Prefs)
    participant StateSvc as EditorStateSnapshot
    participant Server as MCP Server (batch_execute)
    participant Skill as unity-mcp-skill / Client

    Skill->>Server: POST batch_execute(commands[])
    Server->>StateSvc: Read latest editor state (settings)
    StateSvc-->>Server: settings.batch_execute_max_commands (or null)
    Server->>Server: determine max_commands (cached or default, clamp to 100)
    Server->>Server: validate len(commands) <= max_commands
    alt valid
        Server-->>Skill: execute commands (non-transactional)
    else too many
        Server-->>Skill: error - exceed configured max
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through prefs and trimmed a path so neat,

Default twenty-five now dances with a hundred's beat.
UI, server, snapshot — a trio in tune,
Commands now bound with a configurable rune.
Hooray! the rabbit cheers — batch limits in bloom!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is partially related to the changeset but lacks specificity. It refers to two real aspects of the change (a bug fix and batch customization), but does not clearly convey the primary or most important change. Consider a more specific title such as 'Fix GitUrlOverride path and make batch_execute limit configurable' to clarify the exact nature of both changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description covers the main objectives, includes change types and testing evidence, and indicates documentation updates were completed, but lacks detailed enumeration of changes made.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The local server path resolution logic is duplicated between AssetPathUtility.ResolveLocalServerPath and McpAdvancedSection.ResolveServerPath; consider centralizing this into a single shared helper to avoid future divergence in behavior.
  • ResolveServerPath hardcodes the "file://" prefix while ResolveLocalServerPath preserves the original casing/prefix substring; aligning these behaviors would reduce subtle inconsistencies when users enter differently cased file URLs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The local server path resolution logic is duplicated between AssetPathUtility.ResolveLocalServerPath and McpAdvancedSection.ResolveServerPath; consider centralizing this into a single shared helper to avoid future divergence in behavior.
- ResolveServerPath hardcodes the "file://" prefix while ResolveLocalServerPath preserves the original casing/prefix substring; aligning these behaviors would reduce subtle inconsistencies when users enter differently cased file URLs.

## Individual Comments

### Comment 1
<location> `MCPForUnity/Editor/Helpers/AssetPathUtility.cs:256-257` </location>
<code_context>
+                return path;
+            }
+
+            // If it looks like a PyPI package reference (no path separators), skip
+            if (!path.Contains('/') && !path.Contains('\\') && !path.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
+            {
+                return path;
</code_context>

<issue_to_address>
**suggestion:** Relative local paths without separators are treated as PyPI references and skipped.

This logic treats any string without `/`, `\`, or `file:` as a PyPI reference, so relative directories like `Server` or `my_package` won’t be auto-corrected even if they contain a valid `pyproject.toml`. If users are likely to enter such relative paths, consider tightening the heuristic (e.g. require a more PyPI-specific pattern or explicitly distinguish absolute vs relative paths) so these local directories can still be handled.
</issue_to_address>

### Comment 2
<location> `MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs:353-362` </location>
<code_context>
+        private static string ResolveServerPath(string path)
</code_context>

<issue_to_address>
**suggestion:** Path resolution logic is duplicated between ResolveServerPath and ResolveLocalServerPath and may drift over time.

These two methods share most of the same concerns (local vs URL detection, `file://` handling, `Server` subdirectory probing) but differ in details like the PyPI-style heuristic and prefix preservation. Consider extracting a shared helper for the common resolution logic that both methods delegate to, so any future changes are made in one place and behavior stays consistent across the UI, editor prefs, and `GetMcpServerPackageSource`.

Suggested implementation:

```csharp
        /// <summary>
        /// Shared normalization logic for resolving server paths (both local and remote).
        /// Handles scheme detection, `file://` URIs, and normalization of local filesystem
        /// paths before any call-site specific heuristics are applied.
        /// </summary>
        /// <param name="path">Raw user-provided path or URL.</param>
        /// <param name="normalizedPath">
        /// Normalized local filesystem path (if applicable). When this returns false,
        /// this will be null and the original <paramref name="path" /> should be used.
        /// </param>
        /// <returns>
        /// True when <paramref name="path" /> represents a local filesystem location that
        /// should be further resolved (e.g. probing for a "Server" subdirectory); false
        /// when the caller should treat the original string as-is (URLs, PyPI-style refs, etc.).
        /// </returns>
        private static bool TryNormalizeLocalServerPath(string path, out string normalizedPath)
        {
            normalizedPath = null;

            if (string.IsNullOrEmpty(path))
                return false;

            // Non-local sources (git URLs, PyPI refs, etc.) are returned as-is at call sites.
            if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
                path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
                path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) ||
                path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase))
            {
                return false;
            }

            // Support `file://` style URIs, which can come from Unity file pickers on some platforms.
            if (path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
            {
                if (Uri.TryCreate(path, UriKind.Absolute, out var uri) && uri.IsFile)
                    normalizedPath = uri.LocalPath;
                else
                    normalizedPath = path;

                return true;
            }

            // For plain paths, just normalize separators; call sites can perform additional
            // probing (e.g. for "Server" subdirectories or pyproject.toml detection).
            normalizedPath = path.Replace('\\', Path.DirectorySeparatorChar)
                                 .Replace('/', Path.DirectorySeparatorChar);

            return true;
        }

        /// <summary>
        /// Validates and auto-corrects a local server path to ensure it points to the directory
        /// containing pyproject.toml (the Python package root). If the user selects a parent
        /// directory (e.g. the repo root), this checks for a "Server" subdirectory with
        /// pyproject.toml and returns that instead.
        /// </summary>
        private static string ResolveServerPath(string path)

```

To complete the refactor and fully address the duplication / drift risk, you should:

1. Update `ResolveServerPath` to:
   - Call `TryNormalizeLocalServerPath(path, out var normalizedPath)`.
   - If it returns false, immediately return the original `path` (covers URLs, PyPI-style refs, etc.).
   - Use `normalizedPath` for all subsequent filesystem probing logic (including the `"Server"` subdirectory and `pyproject.toml` detection) instead of re-implementing scheme checks.
2. Update `ResolveLocalServerPath` in the same file to:
   - Call `TryNormalizeLocalServerPath` up-front and branch the same way.
   - Remove any duplicated scheme / `file://` / separator normalization logic that is now handled centrally.
3. If there are other call sites that need consistent behavior (e.g. `GetMcpServerPackageSource` or editor prefs code that performs similar path handling), consider routing them through either `TryNormalizeLocalServerPath` directly or one of the two higher-level resolvers, rather than re-implementing normalization in-line.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +256 to +257
// If it looks like a PyPI package reference (no path separators), skip
if (!path.Contains('/') && !path.Contains('\\') && !path.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Relative local paths without separators are treated as PyPI references and skipped.

This logic treats any string without /, \, or file: as a PyPI reference, so relative directories like Server or my_package won’t be auto-corrected even if they contain a valid pyproject.toml. If users are likely to enter such relative paths, consider tightening the heuristic (e.g. require a more PyPI-specific pattern or explicitly distinguish absolute vs relative paths) so these local directories can still be handled.

Comment on lines +353 to +362
private static string ResolveServerPath(string path)
{
if (string.IsNullOrEmpty(path))
return path;

// If path is not a local filesystem path, return as-is (git URLs, PyPI refs, etc.)
if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase))
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Path resolution logic is duplicated between ResolveServerPath and ResolveLocalServerPath and may drift over time.

These two methods share most of the same concerns (local vs URL detection, file:// handling, Server subdirectory probing) but differ in details like the PyPI-style heuristic and prefix preservation. Consider extracting a shared helper for the common resolution logic that both methods delegate to, so any future changes are made in one place and behavior stays consistent across the UI, editor prefs, and GetMcpServerPackageSource.

Suggested implementation:

        /// <summary>
        /// Shared normalization logic for resolving server paths (both local and remote).
        /// Handles scheme detection, `file://` URIs, and normalization of local filesystem
        /// paths before any call-site specific heuristics are applied.
        /// </summary>
        /// <param name="path">Raw user-provided path or URL.</param>
        /// <param name="normalizedPath">
        /// Normalized local filesystem path (if applicable). When this returns false,
        /// this will be null and the original <paramref name="path" /> should be used.
        /// </param>
        /// <returns>
        /// True when <paramref name="path" /> represents a local filesystem location that
        /// should be further resolved (e.g. probing for a "Server" subdirectory); false
        /// when the caller should treat the original string as-is (URLs, PyPI-style refs, etc.).
        /// </returns>
        private static bool TryNormalizeLocalServerPath(string path, out string normalizedPath)
        {
            normalizedPath = null;

            if (string.IsNullOrEmpty(path))
                return false;

            // Non-local sources (git URLs, PyPI refs, etc.) are returned as-is at call sites.
            if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
                path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
                path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) ||
                path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase))
            {
                return false;
            }

            // Support `file://` style URIs, which can come from Unity file pickers on some platforms.
            if (path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
            {
                if (Uri.TryCreate(path, UriKind.Absolute, out var uri) && uri.IsFile)
                    normalizedPath = uri.LocalPath;
                else
                    normalizedPath = path;

                return true;
            }

            // For plain paths, just normalize separators; call sites can perform additional
            // probing (e.g. for "Server" subdirectories or pyproject.toml detection).
            normalizedPath = path.Replace('\\', Path.DirectorySeparatorChar)
                                 .Replace('/', Path.DirectorySeparatorChar);

            return true;
        }

        /// <summary>
        /// Validates and auto-corrects a local server path to ensure it points to the directory
        /// containing pyproject.toml (the Python package root). If the user selects a parent
        /// directory (e.g. the repo root), this checks for a "Server" subdirectory with
        /// pyproject.toml and returns that instead.
        /// </summary>
        private static string ResolveServerPath(string path)

To complete the refactor and fully address the duplication / drift risk, you should:

  1. Update ResolveServerPath to:
    • Call TryNormalizeLocalServerPath(path, out var normalizedPath).
    • If it returns false, immediately return the original path (covers URLs, PyPI-style refs, etc.).
    • Use normalizedPath for all subsequent filesystem probing logic (including the "Server" subdirectory and pyproject.toml detection) instead of re-implementing scheme checks.
  2. Update ResolveLocalServerPath in the same file to:
    • Call TryNormalizeLocalServerPath up-front and branch the same way.
    • Remove any duplicated scheme / file:// / separator normalization logic that is now handled centrally.
  3. If there are other call sites that need consistent behavior (e.g. GetMcpServerPackageSource or editor prefs code that performs similar path handling), consider routing them through either TryNormalizeLocalServerPath directly or one of the two higher-level resolvers, rather than re-implementing normalization in-line.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@Server/src/services/tools/batch_execute.py`:
- Around line 23-53: The module cache _cached_max_commands is never invalidated
causing stale limits; modify _get_max_commands_from_editor_state to use a
time-based TTL: add a module-level timestamp (e.g.,
_cached_max_commands_last_updated: float | None) and a TTL constant (e.g.,
CACHE_TTL_SECONDS = 60), and when called check if _cached_max_commands is set
and (time.time() - _cached_max_commands_last_updated) < CACHE_TTL_SECONDS then
return the cached value; otherwise fetch from get_editor_state, update both
_cached_max_commands and _cached_max_commands_last_updated when the fetched
limit is valid, and fall back to DEFAULT_MAX_COMMANDS_PER_BATCH on errors; keep
the existing invalidate_cached_max_commands() but no longer required if TTL is
used.
🧹 Nitpick comments (3)
MCPForUnity/Editor/Helpers/AssetPathUtility.cs (1)

214-221: Side-effect in a getter-like method — consider documenting or renaming.

GetMcpServerPackageSource() now mutates EditorPrefs as a side-effect (persisting the corrected path). While this is useful for self-healing, callers may not expect a "Get" method to write state. The inline comment helps, but a brief <remarks> in the XML doc would make the contract clearer for future maintainers.

MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs (1)

347-393: Duplicate logic — delegate to AssetPathUtility.ResolveLocalServerPath instead.

This method is nearly identical to AssetPathUtility.ResolveLocalServerPath (lines 242–286) but with subtle differences: it's missing the PyPI-reference guard and hardcodes the "file://" prefix instead of preserving original casing. Maintaining two copies invites drift.

♻️ Proposed refactor
-        private static string ResolveServerPath(string path)
-        {
-            if (string.IsNullOrEmpty(path))
-                return path;
-
-            // If path is not a local filesystem path, return as-is (git URLs, PyPI refs, etc.)
-            if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
-                path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
-                path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) ||
-                path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase))
-            {
-                return path;
-            }
-
-            // Strip file:// prefix for filesystem checks, but preserve it for the return value
-            string checkPath = path;
-            string prefix = string.Empty;
-            if (checkPath.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
-            {
-                prefix = "file://";
-                checkPath = checkPath.Substring(7);
-            }
-
-            // Already points to a directory with pyproject.toml — correct path
-            if (File.Exists(Path.Combine(checkPath, "pyproject.toml")))
-            {
-                return path;
-            }
-
-            // Check if "Server" subdirectory contains pyproject.toml (common repo structure)
-            string serverSubDir = Path.Combine(checkPath, "Server");
-            if (File.Exists(Path.Combine(serverSubDir, "pyproject.toml")))
-            {
-                string corrected = prefix + serverSubDir;
-                McpLog.Info($"Auto-corrected server path to 'Server' subdirectory: {corrected}");
-                return corrected;
-            }
-
-            // Return as-is; uvx will report the error if the path is invalid
-            return path;
-        }
+        private static string ResolveServerPath(string path)
+        {
+            string resolved = AssetPathUtility.ResolveLocalServerPath(path);
+            if (resolved != path)
+            {
+                McpLog.Info($"Auto-corrected server path to 'Server' subdirectory: {resolved}");
+            }
+            return resolved;
+        }
MCPForUnity/Editor/Tools/BatchExecute.cs (1)

33-51: Consider using ToolParams for parameter validation.

The new null-check and existing parameter access (@params["commands"], @params.Value<bool?>()) bypass ToolParams, which provides consistent validation via methods like GetInt(), RequireString(). Since this is a pre-existing pattern, converting the whole method is outside the scope of this PR, but worth noting for a follow-up.

As per coding guidelines: MCPForUnity/Editor/Tools/*.cs: Use ToolParams for consistent parameter validation in C# Editor tools with methods like GetInt(), RequireString().

Comment on lines +23 to +53
# Module-level cache for the Unity-configured limit (populated from editor state).
_cached_max_commands: int | None = None


async def _get_max_commands_from_editor_state(ctx: Context) -> int:
"""
Attempt to read the configured batch limit from the Unity editor state.
Falls back to DEFAULT_MAX_COMMANDS_PER_BATCH if unavailable.
"""
global _cached_max_commands
if _cached_max_commands is not None:
return _cached_max_commands

try:
from services.resources.editor_state import get_editor_state

state_resp = await get_editor_state(ctx)
data = state_resp.data if hasattr(state_resp, "data") else (
state_resp.get("data") if isinstance(state_resp, dict) else None
)
if isinstance(data, dict):
settings = data.get("settings")
if isinstance(settings, dict):
limit = settings.get("batch_execute_max_commands")
if isinstance(limit, int) and 1 <= limit <= ABSOLUTE_MAX_COMMANDS_PER_BATCH:
_cached_max_commands = limit
return limit
except Exception as exc:
logger.debug("Could not read batch limit from editor state: %s", exc)

return DEFAULT_MAX_COMMANDS_PER_BATCH
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n 'invalidate_cached_max_commands' --type=py

Repository: CoplayDev/unity-mcp

Length of output: 154


🏁 Script executed:

# Also check for any TTL or time-based cache invalidation mechanisms
rg -n 'cache.*expire|ttl|time.*cache|datetime.*cache' -i --type=py

Repository: CoplayDev/unity-mcp

Length of output: 3797


🏁 Script executed:

# Check the full context of batch_execute.py to see if there are cache expiration patterns
fd batch_execute.py --type=f | head -1 | xargs wc -l

Repository: CoplayDev/unity-mcp

Length of output: 109


🏁 Script executed:

# Read the full batch_execute.py to see all cache-related code
cat -n Server/src/services/tools/batch_execute.py

Repository: CoplayDev/unity-mcp

Length of output: 6414


🏁 Script executed:

# Search for places where invalidate_cached_max_commands might be called implicitly or where cache is reset
rg -n 'invalidate|_cached_max_commands' --type=py -B2 -A2

Repository: CoplayDev/unity-mcp

Length of output: 3742


🏁 Script executed:

# Check if there's any event-based mechanism or editor state change listener that might trigger cache invalidation
rg -n 'editor.*state|on_.*change|listener|subscribe|event' --type=py | grep -i 'batch_execute\|max_commands\|cache'

Repository: CoplayDev/unity-mcp

Length of output: 1101


🏁 Script executed:

# Final verification: search for any imports of invalidate_cached_max_commands
rg -n 'from.*batch_execute.*import|import.*batch_execute' --type=py

Repository: CoplayDev/unity-mcp

Length of output: 45


🏁 Script executed:

# Check if there's any test coverage that exercises cache invalidation
fd '.*batch.*execute.*test' -i --type=f

Repository: CoplayDev/unity-mcp

Length of output: 45


🏁 Script executed:

# Broader search: look for any mention of batch_execute in test files
fd --type f -e py | xargs grep -l 'batch_execute' | head -20

Repository: CoplayDev/unity-mcp

Length of output: 228


🏁 Script executed:

# Check if invalidate_cached_max_commands is called via string reference or dynamic import
rg -n 'batch_execute\|invalidate' --type=py Server/tests/ 2>/dev/null | head -30

Repository: CoplayDev/unity-mcp

Length of output: 45


🏁 Script executed:

# Check the files that mention batch_execute to see if any call invalidate_cached_max_commands
for file in "Server/src/cli/commands/batch.py" "Server/src/services/resources/editor_state.py" "Server/src/services/resources/gameobject.py"; do
  echo "=== $file ==="
  grep -n 'invalidate_cached_max_commands' "$file" 2>/dev/null || echo "No matches"
done

Repository: CoplayDev/unity-mcp

Length of output: 242


🏁 Script executed:

# Check if batch.py or any other file imports or uses invalidate_cached_max_commands
grep -r 'invalidate_cached_max_commands' Server/ --include="*.py"

Repository: CoplayDev/unity-mcp

Length of output: 151


Cache is never invalidated — setting changes require server restart.

_cached_max_commands is populated on the first batch_execute call and never cleared. If a user changes the max commands in the Unity MCP Tools window, the server continues using the stale cached value until restart. The invalidate_cached_max_commands() function exists but is not called anywhere in the codebase.

Consider either:

  1. Wiring invalidate_cached_max_commands() to editor state change events, or
  2. Adding a TTL so the cache expires after a reasonable interval (e.g., 30–60 seconds).
🧰 Tools
🪛 Ruff (0.15.0)

[warning] 50-50: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
In `@Server/src/services/tools/batch_execute.py` around lines 23 - 53, The module
cache _cached_max_commands is never invalidated causing stale limits; modify
_get_max_commands_from_editor_state to use a time-based TTL: add a module-level
timestamp (e.g., _cached_max_commands_last_updated: float | None) and a TTL
constant (e.g., CACHE_TTL_SECONDS = 60), and when called check if
_cached_max_commands is set and (time.time() -
_cached_max_commands_last_updated) < CACHE_TTL_SECONDS then return the cached
value; otherwise fetch from get_editor_state, update both _cached_max_commands
and _cached_max_commands_last_updated when the fetched limit is valid, and fall
back to DEFAULT_MAX_COMMANDS_PER_BATCH on errors; keep the existing
invalidate_cached_max_commands() but no longer required if TTL is used.

Copy link
Contributor

Copilot AI left a comment

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 fixes local server-source override path selection to reliably target the Python package root (Server/), and makes the batch_execute command limit configurable up to a hard max of 100 (surfaced from Unity editor state and enforced on both Unity + server). It also expands the Unity-MCP skill documentation with UI creation workflows and updated batch-limit guidance.

Changes:

  • Auto-correct local GitUrlOverride/server-source paths to the Server subdirectory when pyproject.toml is found there.
  • Add a Unity Editor preference UI for batch_execute max commands (default 25, hard max 100) and expose it via editor_state.settings.
  • Update skill docs: new “UI Creation Workflows” section and updated batch limit guidance.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs Auto-corrects server path input and improves browse dialog text for selecting the Server folder.
MCPForUnity/Editor/Helpers/AssetPathUtility.cs Resolves and persists corrected local server package source paths (prefers Server/ when applicable).
MCPForUnity/Editor/Constants/EditorPrefKeys.cs Adds an EditorPrefs key for the configurable batch limit.
MCPForUnity/Editor/Windows/Components/Tools/McpToolsSection.cs Adds UI control in the Tools window to set max commands per batch.
MCPForUnity/Editor/Tools/BatchExecute.cs Enforces the configurable batch limit on the Unity side (default 25, max 100).
MCPForUnity/Editor/Services/EditorStateCache.cs Publishes the configured batch limit into the editor_state snapshot under settings.
Server/src/services/resources/editor_state.py Extends the editor_state schema to include settings.batch_execute_max_commands.
Server/src/services/tools/batch_execute.py Reads batch limit from editor_state (with fallback/defaults) and updates validation + tool description.
unity-mcp-skill/references/workflows.md Adds “UI Creation Workflows” templates and references batch limit configurability.
unity-mcp-skill/SKILL.md Updates batch limit docs and adds a UI capability row linking to the UI workflows section.
.claude/skills/unity-mcp-skill/references/workflows.md Mirrors the UI workflows documentation update for the Claude skill copy.
.claude/skills/unity-mcp-skill/SKILL.md Mirrors the SKILL.md documentation update for the Claude skill copy.
Comments suppressed due to low confidence (1)

MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs:343

  • OnBrowseGitUrlClicked() sets gitUrlOverride.value, which triggers the registered value-changed callback that already persists to EditorPrefs and invokes OnGitUrlChanged/OnHttpServerCommandUpdateRequested. The method then repeats those side effects, causing duplicate pref writes and double event invocation. Use SetValueWithoutNotify() here and keep the explicit persistence/events, or set .value and remove the explicit EditorPrefs.SetString(...) + event invocations.
        private void OnBrowseGitUrlClicked()
        {
            string picked = EditorUtility.OpenFolderPanel("Select Server folder (containing pyproject.toml)", string.Empty, string.Empty);
            if (!string.IsNullOrEmpty(picked))
            {
                picked = ResolveServerPath(picked);
                gitUrlOverride.value = picked;
                EditorPrefs.SetString(EditorPrefKeys.GitUrlOverride, picked);
                OnGitUrlChanged?.Invoke();
                OnHttpServerCommandUpdateRequested?.Invoke();
                McpLog.Info($"Server source override set to: {picked}");

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

Comment on lines +23 to +49
# Module-level cache for the Unity-configured limit (populated from editor state).
_cached_max_commands: int | None = None


async def _get_max_commands_from_editor_state(ctx: Context) -> int:
"""
Attempt to read the configured batch limit from the Unity editor state.
Falls back to DEFAULT_MAX_COMMANDS_PER_BATCH if unavailable.
"""
global _cached_max_commands
if _cached_max_commands is not None:
return _cached_max_commands

try:
from services.resources.editor_state import get_editor_state

state_resp = await get_editor_state(ctx)
data = state_resp.data if hasattr(state_resp, "data") else (
state_resp.get("data") if isinstance(state_resp, dict) else None
)
if isinstance(data, dict):
settings = data.get("settings")
if isinstance(settings, dict):
limit = settings.get("batch_execute_max_commands")
if isinstance(limit, int) and 1 <= limit <= ABSOLUTE_MAX_COMMANDS_PER_BATCH:
_cached_max_commands = limit
return limit
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

_cached_max_commands is cached at module scope and not keyed by Unity instance. Since the server supports multiple Unity instances via unity_instance context state, a limit read from one project can incorrectly apply to another (rejecting valid batches or allowing batches Unity will reject). Cache this per unity_instance (e.g., dict keyed by instance id) or remove the cache and read editor_state each call.

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +59
async def _get_max_commands_from_editor_state(ctx: Context) -> int:
"""
Attempt to read the configured batch limit from the Unity editor state.
Falls back to DEFAULT_MAX_COMMANDS_PER_BATCH if unavailable.
"""
global _cached_max_commands
if _cached_max_commands is not None:
return _cached_max_commands

try:
from services.resources.editor_state import get_editor_state

state_resp = await get_editor_state(ctx)
data = state_resp.data if hasattr(state_resp, "data") else (
state_resp.get("data") if isinstance(state_resp, dict) else None
)
if isinstance(data, dict):
settings = data.get("settings")
if isinstance(settings, dict):
limit = settings.get("batch_execute_max_commands")
if isinstance(limit, int) and 1 <= limit <= ABSOLUTE_MAX_COMMANDS_PER_BATCH:
_cached_max_commands = limit
return limit
except Exception as exc:
logger.debug("Could not read batch limit from editor state: %s", exc)

return DEFAULT_MAX_COMMANDS_PER_BATCH


def invalidate_cached_max_commands() -> None:
"""Reset the cached limit so the next call re-reads from editor state."""
global _cached_max_commands
_cached_max_commands = None
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The max-commands limit is cached indefinitely after the first read. If the user changes the setting in the Unity MCP Tools window while the server is running, the server will keep enforcing the old limit until invalidate_cached_max_commands() is called (currently unused). Consider adding a TTL/refresh mechanism, invalidating on editor_state changes, or just re-reading editor_state each request to keep behavior consistent with the UI.

Copilot uses AI. Check for mistakes.
Comment on lines +355 to +392
if (string.IsNullOrEmpty(path))
return path;

// If path is not a local filesystem path, return as-is (git URLs, PyPI refs, etc.)
if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase))
{
return path;
}

// Strip file:// prefix for filesystem checks, but preserve it for the return value
string checkPath = path;
string prefix = string.Empty;
if (checkPath.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
prefix = "file://";
checkPath = checkPath.Substring(7);
}

// Already points to a directory with pyproject.toml — correct path
if (File.Exists(Path.Combine(checkPath, "pyproject.toml")))
{
return path;
}

// Check if "Server" subdirectory contains pyproject.toml (common repo structure)
string serverSubDir = Path.Combine(checkPath, "Server");
if (File.Exists(Path.Combine(serverSubDir, "pyproject.toml")))
{
string corrected = prefix + serverSubDir;
McpLog.Info($"Auto-corrected server path to 'Server' subdirectory: {corrected}");
return corrected;
}

// Return as-is; uvx will report the error if the path is invalid
return path;
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

ResolveServerPath() duplicates the local-path auto-correction logic that was also added to AssetPathUtility.ResolveLocalServerPath(). Keeping two implementations increases the chance of drift (e.g., handling of file:/// URIs, future supported schemes). Prefer reusing the helper from AssetPathUtility (and/or extracting a single shared utility) so both the UI and command-building paths behave identically.

Suggested change
if (string.IsNullOrEmpty(path))
return path;
// If path is not a local filesystem path, return as-is (git URLs, PyPI refs, etc.)
if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase))
{
return path;
}
// Strip file:// prefix for filesystem checks, but preserve it for the return value
string checkPath = path;
string prefix = string.Empty;
if (checkPath.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
prefix = "file://";
checkPath = checkPath.Substring(7);
}
// Already points to a directory with pyproject.toml — correct path
if (File.Exists(Path.Combine(checkPath, "pyproject.toml")))
{
return path;
}
// Check if "Server" subdirectory contains pyproject.toml (common repo structure)
string serverSubDir = Path.Combine(checkPath, "Server");
if (File.Exists(Path.Combine(serverSubDir, "pyproject.toml")))
{
string corrected = prefix + serverSubDir;
McpLog.Info($"Auto-corrected server path to 'Server' subdirectory: {corrected}");
return corrected;
}
// Return as-is; uvx will report the error if the path is invalid
return path;
// Delegate to shared helper to keep path resolution logic centralized
return AssetPathUtility.ResolveLocalServerPath(path);

Copilot uses AI. Check for mistakes.
@Scriptwonder Scriptwonder merged commit ad3756e into CoplayDev:beta Feb 12, 2026
1 of 2 checks passed
Scriptwonder added a commit that referenced this pull request Feb 12, 2026
@Scriptwonder Scriptwonder deleted the bug-fix-and-batch-customization branch February 12, 2026 00:24
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.

1 participant