Skip to content

Comments

fix: strip MCP namespace prefix from tool names in batch_execute#810

Open
dsarno wants to merge 2 commits intoCoplayDev:betafrom
dsarno:fix/batch-execute-strip-mcp-prefix
Open

fix: strip MCP namespace prefix from tool names in batch_execute#810
dsarno wants to merge 2 commits intoCoplayDev:betafrom
dsarno:fix/batch-execute-strip-mcp-prefix

Conversation

@dsarno
Copy link
Collaborator

@dsarno dsarno commented Feb 21, 2026

Summary

  • Clients sending prefixed tool names in batch_execute commands (e.g. UnityMCP:manage_gameobject or mcp__UnityMCP__manage_gameobject) caused "Unknown or unsupported command type" errors because CommandRegistry registers handlers under short names only
  • Adds strip_mcp_prefix() in Python and StripMcpPrefix() in C# to normalize both colon and double-underscore prefix formats before command dispatch
  • Defense-in-depth: stripping happens in both layers so it works regardless of which path the tool name takes

Test plan

  • 8 Python unit tests for strip_mcp_prefix() — all pass
  • 7 C# unit tests for StripMcpPrefix() — all pass via Unity Test Runner
  • Manual verification: use batch_execute with prefixed tool names from Claude Code

🤖 Generated with Claude Code

Summary by Sourcery

Normalize MCP tool names in batch_execute on both the Unity client and Python server to handle namespaced tool identifiers transparently.

Bug Fixes:

  • Fix batch_execute failures when clients send namespaced MCP tool identifiers by stripping known MCP prefixes before command dispatch.

Tests:

  • Add C# edit-mode tests for StripMcpPrefix covering colon, double-underscore, plain, and edge-case tool names.
  • Add Python unit tests for strip_mcp_prefix to validate normalization of colon and double-underscore MCP tool name formats.

Summary by CodeRabbit

  • New Features

    • Batch command execution now normalizes MCP server namespace prefixes in tool names (supports colon-separated and double-underscore formats), improving consistent discovery, validation, and invocation.
  • Tests

    • Added unit tests covering multiple formats, edge cases, null/empty inputs, and multi-segment names to ensure reliable prefix stripping.

Clients may send prefixed tool names inside batch_execute commands
(e.g. "UnityMCP:manage_gameobject" or "mcp__UnityMCP__manage_gameobject")
which caused "Unknown or unsupported command type" errors since
CommandRegistry registers handlers under short names only.

Strips prefixes in both Python (server-side, early) and C# (Unity-side,
defense-in-depth) to handle both colon and double-underscore formats.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 21, 2026

Reviewer's Guide

Normalizes MCP tool names before dispatch in both the Unity C# batch executor and the Python server batch execution path by stripping configurable MCP namespace prefixes, and adds unit tests for the new behavior.

Sequence diagram for MCP tool name normalization in batch_execute

sequenceDiagram
    actor Client
    participant PythonServer as Python_batch_execute
    participant UnityEditor as CSharp_BatchExecute
    participant CommandRegistry

    Client->>PythonServer: batch_execute(commands)
    loop For each command
        PythonServer->>PythonServer: strip_mcp_prefix(tool_name)
        PythonServer->>UnityEditor: HandleCommand(params with tool_name)
        UnityEditor->>UnityEditor: StripMcpPrefix(tool_name)
        UnityEditor->>CommandRegistry: Dispatch(tool_name, params)
        CommandRegistry-->>UnityEditor: result
        UnityEditor-->>PythonServer: result
    end
    PythonServer-->>Client: aggregated results
Loading

Class diagram for updated BatchExecute prefix stripping logic

classDiagram
    class BatchExecute {
        +Task~object~ HandleCommand(JObject params)
        -static bool DetermineCallSucceeded(object result)
        -static JObject NormalizeParameterKeys(JObject source)
        -static string StripMcpPrefix(string toolName)
    }

    class BatchExecutePy {
        +strip_mcp_prefix(str tool_name) str
        +batch_execute(Context ctx, list commands) dict
    }

    BatchExecutePy ..> BatchExecute : sends commands to
    BatchExecute ..> BatchExecute : uses StripMcpPrefix
    BatchExecutePy ..> BatchExecutePy : uses strip_mcp_prefix
Loading

File-Level Changes

Change Details Files
Normalize tool names in Unity batch executor by stripping MCP namespace prefixes before command dispatch.
  • Wrap batch_execute command tool name lookup with a StripMcpPrefix call to ensure only the short tool name is passed to CommandRegistry.
  • Implement StripMcpPrefix to handle both double-underscore (mcp__Server__tool) and colon (Server:tool) formats with defensive null/empty handling and sensible edge-case behavior.
  • Document the prefix-stripping behavior in XML comments to clarify supported formats and intent.
MCPForUnity/Editor/Tools/BatchExecute.cs
Normalize tool names in Python batch_execute service by stripping MCP namespace prefixes, and add coverage tests.
  • Introduce strip_mcp_prefix helper that converts colon and double-underscore prefixed tool names to their short form while preserving underscores in tool names.
  • Call strip_mcp_prefix in batch_execute after validating the tool name but before dispatch, so downstream logic sees only normalized names.
  • Add pytest-based parameterized tests to verify colon, double-underscore, and plain-name behaviors for strip_mcp_prefix.
Server/src/services/tools/batch_execute.py
Server/tests/test_batch_strip_prefix.py
Add Unity edit-mode tests validating StripMcpPrefix behavior across common and edge-case inputs.
  • Create NUnit test fixture BatchExecuteStripPrefixTests covering plain names, colon-prefixed names, double-underscore-prefixed names, and tools with underscores.
  • Add tests for null/empty inputs, colon-only strings, and multiple-colon inputs to document and lock in edge-case semantics.
  • Register the new test asset via its associated .meta file so it is recognized by Unity’s asset pipeline.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/BatchExecuteStripPrefixTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/BatchExecuteStripPrefixTests.cs.meta

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 21, 2026

📝 Walkthrough

Walkthrough

Adds a helper to normalize MCP-prefixed tool names (supports "mcp__Server__tool" and "Server:tool") and integrates it into batch execution flows in both C# and Python; tests added in both projects to validate various formats and edge cases.

Changes

Cohort / File(s) Summary
C# Implementation
MCPForUnity/Editor/Tools/BatchExecute.cs
Adds internal static string StripMcpPrefix(string toolName) and uses it when extracting tool names from commands to normalize prefixes before discovery/validation.
C# Tests
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/BatchExecuteStripPrefixTests.cs, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/BatchExecuteStripPrefixTests.cs.meta
Adds NUnit test class with 7 tests covering plain names, colon format, double-underscore format, edge cases (null, empty, colon-only, multiple colons) and preservation of tool-name underscores.
Python Implementation
Server/src/services/tools/batch_execute.py
Adds def strip_mcp_prefix(tool_name: str) -> str and applies it to each command during batch_execute preprocessing to normalize prefixed tool names.
Python Tests
Server/tests/test_batch_strip_prefix.py
Adds pytest parametrized tests covering colon and double-underscore formats and edge cases to verify normalization behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • msanatan

Poem

🐰 I nibbled prefixes from morn until night,
Colon and underscores now vanish from sight,
Unity and Server both tidy and spry,
Tests give a hop and a jubilant cry,
Tools stripped and ready — hooray, let’s compile! 🥕✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description covers the problem, solution, test coverage, and includes detailed summary with code generation attribution; however, it does not follow the provided template structure with Type of Change and other required sections. Restructure the description to follow the template by adding explicit Type of Change section (Bug fix), Changes Made list, Documentation Updates checklist, and Related Issues section.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: adding MCP namespace prefix stripping to batch_execute across both C# and Python implementations.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 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 1 issue, and left some high level feedback:

  • The C# and Python StripMcpPrefix/strip_mcp_prefix implementations differ for edge cases like a bare colon (":"): C# intentionally passes it through unchanged while Python returns an empty string, so it would be good to align the semantics (and add a corresponding Python test) to avoid subtle inconsistencies between the two paths.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The C# and Python `StripMcpPrefix`/`strip_mcp_prefix` implementations differ for edge cases like a bare colon (`":"`): C# intentionally passes it through unchanged while Python returns an empty string, so it would be good to align the semantics (and add a corresponding Python test) to avoid subtle inconsistencies between the two paths.

## Individual Comments

### Comment 1
<location> `MCPForUnity/Editor/Tools/BatchExecute.cs:227-230` </location>
<code_context>
+                return toolName;
+
+            // Double-underscore format: "mcp__ServerName__tool_name"
+            if (toolName.StartsWith("mcp__"))
+            {
+                int secondSep = toolName.IndexOf("__", 5, StringComparison.Ordinal);
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Specify StringComparison for StartsWith to match the IndexOf usage and avoid culture-dependent behavior.

Since the subsequent `IndexOf` uses `StringComparison.Ordinal`, use the same on `StartsWith` for consistency and to avoid culture-dependent behavior, e.g. `toolName.StartsWith("mcp__", StringComparison.Ordinal)`.

```suggestion
            // Double-underscore format: "mcp__ServerName__tool_name"
            if (toolName.StartsWith("mcp__", StringComparison.Ordinal))
            {
                int secondSep = toolName.IndexOf("__", 5, StringComparison.Ordinal);
```
</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.

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

🧹 Nitpick comments (1)
Server/tests/test_batch_strip_prefix.py (1)

7-25: Consider adding edge-case tests to match C# suite coverage.

The C# BatchExecuteStripPrefixTests covers null/empty, colon-only (":"), and multi-colon inputs. Python's suite omits these. While all three pass through as invalid tool names in practice, covering them would document the intended behaviour (and would have caught the ":" divergence noted above).

➕ Suggested additional test cases
         # Tool name itself contains underscores — should be preserved
         ("mcp__UnityMCP__manage_scriptable_object", "manage_scriptable_object"),
+        # Edge cases
+        ("", ""),
+        # Colon-only: no valid suffix — passes through unchanged (will fail at CommandRegistry)
+        (":", ":"),
+        # Trailing colon: no valid suffix — passes through unchanged
+        ("Server:", "Server:"),
+        # Multiple colons: last segment used
+        ("a:b:manage_gameobject", "manage_gameobject"),
     ],
 )

Note: the expected values for ":" and "Server:" assume the guard fix proposed in batch_execute.py is applied; without it the current implementation returns "" for both.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Server/tests/test_batch_strip_prefix.py` around lines 7 - 25, Add edge-case
params to test_strip_mcp_prefix to cover null/None, empty, colon-only, and
multi-colon inputs: update the param list for strip_mcp_prefix to include (None,
None), ("", ""), (":", ":"), ("Server:", "Server:"), and a multi-colon case like
("a::b", "a::b"); update the test signature to accept Optional[str] (e.g.,
input_name: Optional[str]) so pytest can pass None and ensure strip_mcp_prefix
preserves these inputs as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Server/src/services/tools/batch_execute.py`:
- Around line 36-41: strip_mcp_prefix can return an empty string for inputs with
trailing separators (e.g. "mcp__Server__" or "Server:"), unlike the C#
StripMcpPrefix which returns the original input when no characters follow the
separator; update strip_mcp_prefix to validate that the second "__" found (when
handling "mcp__") has at least one character after it and that the ":" when
using rsplit has at least one character after it, and if not return the original
tool_name unchanged (i.e., before the split/rsplit), so the Python behavior
matches the C# guards and the empty result is not propagated.

---

Nitpick comments:
In `@Server/tests/test_batch_strip_prefix.py`:
- Around line 7-25: Add edge-case params to test_strip_mcp_prefix to cover
null/None, empty, colon-only, and multi-colon inputs: update the param list for
strip_mcp_prefix to include (None, None), ("", ""), (":", ":"), ("Server:",
"Server:"), and a multi-colon case like ("a::b", "a::b"); update the test
signature to accept Optional[str] (e.g., input_name: Optional[str]) so pytest
can pass None and ensure strip_mcp_prefix preserves these inputs as-is.

- Add StringComparison.Ordinal to StartsWith in C# (Sourcery)
- Guard against empty suffix from trailing separators in Python to
  match C# semantics (CodeRabbit)
- Add edge-case tests for ":", "Server:", "mcp__Server__", "" inputs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

🧹 Nitpick comments (1)
Server/tests/test_batch_strip_prefix.py (1)

7-33: Good coverage including edge cases; consider one more scenario.

The parametrized cases are thorough, especially the trailing-separator and underscore-preservation tests. One potentially useful addition: a name with mcp__ prefix but no second __ separator (e.g., "mcp__toolname") — the function should return it unchanged since the "__" in tool_name[5:] guard fails.

Suggested addition
         # Multiple colons: last segment used
         ("a:b:manage_gameobject", "manage_gameobject"),
+        # mcp__ prefix without a second separator passes through unchanged
+        ("mcp__toolname", "mcp__toolname"),
     ],
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Server/tests/test_batch_strip_prefix.py` around lines 7 - 33, Add a
parametrized case to test_strip_mcp_prefix that covers an input beginning with
"mcp__" but lacking a second "__" separator (e.g., "mcp__toolname") and assert
strip_mcp_prefix returns it unchanged; locate the parametrized list in
Server/tests/test_batch_strip_prefix.py and add this tuple alongside the other
cases to ensure strip_mcp_prefix's guard for tool_name[5:] is exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Server/tests/test_batch_strip_prefix.py`:
- Around line 7-33: Add a parametrized case to test_strip_mcp_prefix that covers
an input beginning with "mcp__" but lacking a second "__" separator (e.g.,
"mcp__toolname") and assert strip_mcp_prefix returns it unchanged; locate the
parametrized list in Server/tests/test_batch_strip_prefix.py and add this tuple
alongside the other cases to ensure strip_mcp_prefix's guard for tool_name[5:]
is exercised.

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