Skip to content

Feature: API verification via doc/asset#939

Merged
Scriptwonder merged 18 commits intoCoplayDev:betafrom
Scriptwonder:feat/unity-api-verification-tools
Mar 16, 2026
Merged

Feature: API verification via doc/asset#939
Scriptwonder merged 18 commits intoCoplayDev:betafrom
Scriptwonder:feat/unity-api-verification-tools

Conversation

@Scriptwonder
Copy link
Collaborator

@Scriptwonder Scriptwonder commented Mar 16, 2026

Add two new MCP tools (unity_reflect and unity_docs) that let AI assistants verify Unity API signatures via live C# reflection and fetch official documentation on demand, reducing hallucinated/outdated API answers. Inspired by https://github.com/Razpines/UnityRAG

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Documentation update
  • Test update

Changes Made

  • unity_reflect — Queries the running Unity Editor's C# APIs via reflection. Actions: get_type (member summary), get_member (full signatures), search (fuzzy type name search across assemblies). Handles namespace resolution, ambiguity detection, extension methods, generic types.
  • unity_docs — Fetches official Unity documentation from docs.unity3d.com. Actions: get_doc (ScriptReference), get_manual (Manual pages), get_package_doc (package docs with redirect tracking), lookup (parallel search across all sources + auto project asset search for shader/material queries).
  • New docs tool group (opt-in via manage_tools)
  • CLI commands: reflect type/member/search, docs get
  • MCP server instructions nudging AI to verify APIs before answering
  • HTML parser supports both legacy and modern Unity docs format

Testing/Screenshots/Recordings

  • Live tested: unity_reflect search "NavMesh", unity_reflect get_member "NavMeshAgent" "SetDestination", unity_docs lookup "Physics.Raycast,NavMeshAgent,execution-order" (batch), unity_docs get_package_doc with URP redirect tracking

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)

Additional Notes

  • Trust hierarchy: reflection > project assets > docs (docs can be outdated)
  • unity_docs is Python-only (no C# handler) — uses unity_target="unity_reflect" for HTTP mode visibility
  • lookup auto-searches project assets when query contains shader/material/texture keywords
  • Discovered during testing: Unity docs claim "no 2D light interoperability with MeshRenderer" but Mesh2D-Lit-Default.shader exists in URP — proving why reflection + asset search matters over docs alone

Summary by Sourcery

Add new Unity API verification and documentation tools, expose them via MCP, CLI, and tool groups, and document workflows for using them to reduce hallucinated or outdated Unity API usage.

New Features:

  • Introduce unity_reflect MCP tool to inspect live Unity C# APIs via reflection for type/member inspection and search.
  • Introduce unity_docs MCP tool to fetch and aggregate Unity ScriptReference, Manual, and package documentation, with optional asset search for shader/material queries.
  • Add CLI commands for reflection and documentation lookup to support terminal-based API verification workflows.

Enhancements:

  • Create an opt-in docs tool group for API reflection and documentation lookup and wire new tools into the manifest and tool registry.
  • Extend server instructions to encourage using reflection, docs, and asset search for Unity API verification, especially for shaders, packages, and version-specific changes.
  • Add new API verification workflows and docs tooling sections in English and Chinese documentation and tool references.

Tests:

  • Add Python tests for unity_docs covering URL/version handling, HTML parsing for legacy/modern Unity docs, and lookup behavior including asset-related queries.
  • Add Python tests for unity_reflect to validate parameter handling, tool grouping, and transport behavior.
  • Add Unity edit mode tests for the UnityReflect C# tool to validate type resolution, member inspection, search behavior, and generic handling.

Summary by CodeRabbit

  • New Features

    • Added Unity API reflection and documentation lookup tools (type/member search, API/docs/manual/package lookups) and new CLI commands to invoke them.
  • Documentation

    • Expanded tools reference, workflows, and recent updates to cover API verification and usage patterns; localized docs and README updated.
    • Note: the new workflow/docs sections were inadvertently duplicated in some references.
  • Tests

    • Added extensive unit and integration tests covering both tools and parsing/lookup behavior.

Implements get_type, get_member, and search actions via live
C# reflection. Assembly cache with domain reload invalidation,
extension method discovery, ambiguity detection, generic type parsing.
- Register 'docs' group in tool_registry (opt-in, not in defaults)
- unity_reflect wraps get_type/get_member/search actions to Unity C#
- 17 tests covering param validation, routing, scope handling
13 tests covering get_type, get_member, search, generic types,
ambiguity detection, namespace resolution, and error handling.
Fetches + parses Unity ScriptReference HTML from docs.unity3d.com.
Version-aware URL construction, method/property URL fallback,
stdlib html.parser, 28 tests covering extraction and error handling.
reflect CLI routes to Unity (standard pattern).
docs CLI calls Python functions directly (no C# handler).
- CLI docs.py: call _get_doc() directly instead of duplicating fallback logic
- UnityReflect.cs: use UnityTypeResolver.ResolveAny() for primary type resolution
- UnityReflect.cs: cache GetAssemblyTypeCache() before loops (avoid repeated lock)
- UnityReflect.cs: cache ObsoleteAttribute lookup (was called twice per method)
- UnityReflect.cs: use HashSet instead of List for obsolete member dedup
- UnityReflect.cs: reuse assembly cache in FindExtensionMethods
Unity ScriptReference now uses h3 (not h2) for section titles,
"signature-CS sig-block" divs (not <pre> in signature),
"name lbl"/"desc" td classes (not "name-collumn"/"desc-collumn").
Parser now supports both old and new formats. 5 new tests added.
Instructs AI to use unity_reflect/unity_docs and asset search
to verify Unity APIs before answering, rather than relying on
training data. Highlights common hallucination areas.
- get_manual: fetch Unity Manual pages by slug with version fallback
- get_package_doc: fetch package docs with redirect tracking (URP etc.)
- _ManualPageParser for article-style HTML (h1 title, h2/h3 sections)
- _fetch_url_full returns final URL after redirects
- 12 new tests, 1002 total passing
Searches ScriptReference, Manual, and package docs concurrently
via asyncio.gather. Returns all hits with source labels.
5 new tests, 1007 total passing.
MCP clients serialize list params as strings. Changed queries
parameter from list[str] to comma-separated string for compatibility.
lookup now detects asset-related keywords (shader, material, texture,
sprite, lit, urp, etc.) and automatically includes manage_asset search
alongside doc queries. Runs in parallel, fails silently if no Unity
connection.
Previous: used full query as search pattern (e.g., *MeshRenderer 2D
lights URP*) — matched nothing. Now extracts individual terms, strips
stopwords, infers filter_type from keywords (shader→Shader, etc.),
runs multiple focused searches in parallel. Deduplicates results.
- manifest.json: add both tools in alphabetical order
- README.md: add to Available Tools, update Recent Updates
- README-zh.md: mirror changes in Chinese
- SKILL.md: add Docs group to tool categories with trust hierarchy
- tools-reference.md: full reference for both tools with examples
- workflows.md: add 5 API verification workflow patterns
Copilot AI review requested due to automatic review settings March 16, 2026 06:56
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 16, 2026

Reviewer's Guide

Adds a new opt-in docs tool group with two tools, unity_reflect (C# reflection) and unity_docs (HTTP docs fetcher), wires them into the MCP server/CLI/manifest, and documents new API verification workflows and usage patterns, including tests for both server-side handlers and the Unity editor reflection implementation.

Sequence diagram for full Unity API verification workflow

sequenceDiagram
    actor Dev as Developer
    participant Client as MCPClient/CLI
    participant PyRef as unity_reflect(Python)
    participant CSRef as UnityReflect(C#)
    participant PyDocs as unity_docs(Python)
    participant DocsSite as docs.unity3d.com
    participant Assets as manage_asset/ProjectAssets

    Dev->>Client: Ask Unity API question / run reflect/docs command

    rect rgb(230,230,255)
        Note over Client,PyRef: Step 1 — search type
        Client->>PyRef: action=search, query="NavMesh"
        PyRef->>CSRef: unity_reflect {action: search, query}
        CSRef->>CSRef: Scan assemblies by scope
        CSRef-->>PyRef: Matching types (NavMeshAgent, NavMeshPath,...)
        PyRef-->>Client: search results
    end

    rect rgb(230,255,230)
        Note over Client,PyRef: Step 2 — get_type summary
        Client->>PyRef: action=get_type, class_name="UnityEngine.AI.NavMeshAgent"
        PyRef->>CSRef: unity_reflect {action: get_type, class_name}
        CSRef->>CSRef: Reflect methods/properties/fields/events
        CSRef-->>PyRef: Member name lists
        PyRef-->>Client: Type info + member summary
    end

    rect rgb(255,240,230)
        Note over Client,PyRef: Step 3 — get_member signatures
        Client->>PyRef: action=get_member, class_name="NavMeshAgent", member_name="SetDestination"
        PyRef->>CSRef: unity_reflect {action: get_member,...}
        CSRef->>CSRef: Collect overloads, params, return type
        CSRef-->>PyRef: Overload details
        PyRef-->>Client: Full signatures
    end

    rect rgb(230,255,255)
        Note over Client,PyDocs: Step 4 — get documentation
        Client->>PyDocs: action=get_doc, class_name="NavMeshAgent", member_name="SetDestination"
        PyDocs->>DocsSite: HTTP GET ScriptReference URL
        DocsSite-->>PyDocs: HTML page
        PyDocs->>PyDocs: Parse description, signatures, params, examples
        PyDocs-->>Client: Structured doc data
    end

    opt Shader/Material question
        Client->>PyDocs: action=lookup, query includes shader/material terms
        PyDocs->>Assets: manage_asset search (shader/material)
        Assets-->>PyDocs: Matching project assets
        PyDocs-->>Client: Docs hits + project asset matches
    end

    Client-->>Dev: Verified APIs + examples (reflection > assets > docs)
Loading

File-Level Changes

Change Details Files
Introduce server-side unity_docs tool for fetching and parsing Unity documentation (ScriptReference, Manual, and package docs), including batch lookup and asset-aware queries.
  • Implement async HTTP fetching with redirect-aware helper and version extraction for Unity URLs.
  • Add robust HTML parsers for ScriptReference pages (legacy and modern formats) and Manual/package documentation pages.
  • Expose unity_docs MCP tool with actions get_doc, get_manual, get_package_doc, and lookup, including asset-search integration for shader/material-like queries.
  • Add extensive unit tests for unity_docs covering URL building, HTML parsing, action validation, fallbacks, and lookup behavior.
Server/src/services/tools/unity_docs.py
Server/tests/test_unity_docs.py
Add Unity editor-side unity_reflect tool and server proxy for live C# API reflection, plus tests and CLI integration.
  • Implement UnityReflect editor tool in C# that reflects types/members from loaded assemblies, including generic type normalization, namespace resolution, extension method discovery, and ambiguity detection.
  • Expose unity_reflect MCP tool on the server that validates actions/parameters and forwards reflection requests to Unity via the transport layer.
  • Add NUnit edit-mode tests for UnityReflect covering type resolution, ambiguity, members, search, and generics, plus server-side tests for unity_reflect wiring and parameter validation.
  • Add CLI reflect command group (type, member, search) that calls the unity_reflect tool with appropriate parameters.
MCPForUnity/Editor/Tools/UnityReflect.cs
Server/src/services/tools/unity_reflect.py
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/UnityReflectTests.cs
Server/tests/test_unity_reflect.py
Server/src/cli/commands/reflect.py
Add CLI and helper commands for docs retrieval without requiring a Unity connection.
  • Introduce docs CLI group with get subcommand that calls the internal _get_doc helper to fetch ScriptReference docs.
  • Wire new CLI command modules into the optional command registration list so they are available when present.
Server/src/cli/commands/docs.py
Server/src/cli/main.py
Extend tool registry, manifest, and skill metadata to include the new docs tool group and tools.
  • Register docs tool group label and ensure it is not enabled by default, keeping docs tools opt-in.
  • Declare unity_docs and unity_reflect tools in manifest.json so MCP clients can discover them.
  • Update SKILL documentation tables to describe the Docs group, trust hierarchy, and recommended workflow using unity_reflect then unity_docs.
Server/src/services/registry/tool_registry.py
manifest.json
.claude/skills/unity-mcp-skill/SKILL.md
unity-mcp-skill/SKILL.md
Document the new docs tools and API verification workflows in the tool reference, workflows guides, and README (including zh-CN).
  • Add a Docs Tools section to both skill tool-reference files, detailing parameters, actions, and sample usage for unity_reflect and unity_docs.
  • Add an API Verification Workflows section to the workflows docs explaining the trust hierarchy and step-by-step usage patterns, including batch lookups and asset searches.
  • Update English and Chinese READMEs to mention the new tools in the changelog and extend the available tools list to include unity_docs and unity_reflect.
  • Add guidance to the server instructions string about using unity_reflect, unity_docs, and manage_asset to verify APIs before answering Unity questions.
.claude/skills/unity-mcp-skill/references/tools-reference.md
unity-mcp-skill/references/tools-reference.md
.claude/skills/unity-mcp-skill/references/workflows.md
unity-mcp-skill/references/workflows.md
README.md
docs/i18n/README-zh.md
Server/src/main.py

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 Mar 16, 2026

📝 Walkthrough

Walkthrough

Adds two new Unity verification tools—unity_reflect (Unity-side C# reflection handler) and unity_docs (HTTP doc fetchers/parsers)—with server-side tool implementations, CLI commands, Unity tests, Python tests, manifest and documentation updates.

Changes

Cohort / File(s) Summary
Server: Reflection Tool & CLI
Server/src/services/tools/unity_reflect.py, Server/src/cli/commands/reflect.py
New async MCP tool and CLI group "reflect" that validates actions/params (get_type, get_member, search), sends RPCs to Unity, and normalizes responses.
Server: Docs Tool & CLI
Server/src/services/tools/unity_docs.py, Server/src/cli/commands/docs.py
New async MCP tool and CLI group "docs" for version-aware URL building, HTTP fetch, HTML parsing (ScriptReference/Manual/package), parallel lookups and optional Unity asset-search integration.
Server: Integration & Registry
Server/src/cli/main.py, Server/src/services/registry/tool_registry.py, Server/src/main.py
Register reflect/docs CLI commands, add "docs" tool group to registry, and append API verification guidance text.
Unity Editor: Reflection Handler
MCPForUnity/Editor/Tools/UnityReflect.cs, MCPForUnity/Editor/Tools/UnityReflect.cs.meta
New static UnityReflect with HandleCommand(JObject) exposing get_type/get_member/search, caching, extension-method discovery, and assembly-reload cache invalidation.
Tests: Python
Server/tests/test_unity_reflect.py, Server/tests/test_unity_docs.py
Extensive unit tests for both tools covering validation, RPC dispatch, URL construction, HTML parsing, fallbacks, batch lookups, and error cases.
Tests: Unity (EditMode)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/UnityReflectTests.cs, ...UnityReflectTests.cs.meta
NUnit EditMode tests exercising UnityReflect.HandleCommand for type/member resolution, ambiguous matches, generics, and search ranking.
Docs & Skill References
.claude/skills/.../SKILL.md, .claude/skills/.../references/*, unity-mcp-skill/.../SKILL.md, unity-mcp-skill/.../references/*, README.md, docs/i18n/README-zh.md, manifest.json
Documentation and manifest updated to add unity_reflect and unity_docs, new tools/workflows sections; note: duplicate blocks introduced in some docs files (tools/workflows duplicated).

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI / User
    participant Server as Server (unity_reflect)
    participant Unity as Unity Editor
    CLI->>Server: reflect get_type { class_name }
    Server->>Unity: RPC "unity_reflect" with params
    Unity-->>Server: JSON result (type info / members)
    Server-->>CLI: Formatted output (success/data)
Loading
sequenceDiagram
    participant CLI as CLI / User
    participant Server as Server (unity_docs)
    participant Web as docs.unity3d.com
    participant Unity as Unity Editor
    CLI->>Server: docs get_doc { class, member, version? }
    Server->>Web: HTTP GET (version-aware URLs, fallbacks)
    alt Unity context present & asset lookup requested
      Server->>Unity: RPC asset search
      Unity-->>Server: asset results
    end
    Web-->>Server: HTML response
    Server->>Server: parse HTML -> structured dict
    Server-->>CLI: formatted doc result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Unity-MCP skills #659: Overlaps in skill documentation and tools/workflows where unity_reflect and unity_docs entries were previously introduced.
  • Set up Unit tests #220: Related Unity test scaffolding and TestProjects changes that new Unity EditMode tests build upon.

Suggested reviewers

  • dsarno

Poem

🐰 I hopped through code with ears alight,
I peered at types and fetched docs bright.
Reflection gleams and pages found,
Now Unity answers are sound—hop around! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Feature: API verification via doc/asset' is concise and accurately reflects the main feature being added: two new MCP tools for API verification using documentation and asset search capabilities.
Description check ✅ Passed The PR description covers all required template sections: Type of Change, Changes Made, Testing, Documentation Updates, and Additional Notes. It provides comprehensive detail on the two new tools, their actions, CLI commands, and testing performed.

✏️ 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
📝 Coding Plan
  • Generate coding plan for human review comments

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:

  • In unity_docs._search_assets, the broad except Exception: pass swallows all errors silently; consider at least logging via ctx or narrowing the exception types so asset-search failures are visible during debugging without crashing the tool.
  • The CLI docs command calls the internal _get_doc helper directly instead of the public unity_docs tool wrapper, which bypasses validation and future behavior changes; consider routing through unity_docs for consistency with other tools and a single execution path.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `unity_docs._search_assets`, the broad `except Exception: pass` swallows all errors silently; consider at least logging via `ctx` or narrowing the exception types so asset-search failures are visible during debugging without crashing the tool.
- The CLI `docs` command calls the internal `_get_doc` helper directly instead of the public `unity_docs` tool wrapper, which bypasses validation and future behavior changes; consider routing through `unity_docs` for consistency with other tools and a single execution path.

## Individual Comments

### Comment 1
<location path="TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/UnityReflectTests.cs" line_range="197-207" />
<code_context>
+        // ── generic types ───────────────────────────────────────────
+
+        [Test]
+        public void GetType_GenericList_Resolves()
+        {
+            var jo = Invoke("get_type", new JObject { ["class_name"] = "List<T>" });
+
+            Assert.IsTrue((bool)jo["success"]);
+            var data = jo["data"];
+            Assert.IsTrue((bool)data["found"], "List<T> should resolve via generic normalization");
+        }
+    }
</code_context>
<issue_to_address>
**suggestion (testing):** Consider adding a test for multi-parameter generic types (e.g., `Dictionary<TKey, TValue>`) to further validate `NormalizeGenericName`

Right now we only cover the single-parameter path (`List<T> → List�`). Since the normalization logic also handles multiple generic parameters by counting commas with nesting, it would be helpful to add a test that exercises a 2+ parameter case (e.g., `Dictionary<TKey, TValue>`) to ensure that arity detection remains correct.

```suggestion
        // ── generic types ───────────────────────────────────────────

        [Test]
        public void GetType_GenericList_Resolves()
        {
            var jo = Invoke("get_type", new JObject { ["class_name"] = "List<T>" });

            Assert.IsTrue((bool)jo["success"]);
            var data = jo["data"];
            Assert.IsTrue((bool)data["found"], "List<T> should resolve via generic normalization");
        }

        [Test]
        public void GetType_GenericDictionary_Resolves()
        {
            var jo = Invoke("get_type", new JObject { ["class_name"] = "Dictionary<TKey, TValue>" });

            Assert.IsTrue((bool)jo["success"]);
            var data = jo["data"];
            Assert.IsTrue(
                (bool)data["found"],
                "Dictionary<TKey, TValue> should resolve via generic normalization with multiple type parameters"
            );
        }
```
</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 +197 to +207
// ── generic types ───────────────────────────────────────────

[Test]
public void GetType_GenericList_Resolves()
{
var jo = Invoke("get_type", new JObject { ["class_name"] = "List<T>" });

Assert.IsTrue((bool)jo["success"]);
var data = jo["data"];
Assert.IsTrue((bool)data["found"], "List<T> should resolve via generic normalization");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Consider adding a test for multi-parameter generic types (e.g., Dictionary<TKey, TValue>) to further validate NormalizeGenericName

Right now we only cover the single-parameter path (List<T> → List�). Since the normalization logic also handles multiple generic parameters by counting commas with nesting, it would be helpful to add a test that exercises a 2+ parameter case (e.g., Dictionary<TKey, TValue>) to ensure that arity detection remains correct.

Suggested change
// ── generic types ───────────────────────────────────────────
[Test]
public void GetType_GenericList_Resolves()
{
var jo = Invoke("get_type", new JObject { ["class_name"] = "List<T>" });
Assert.IsTrue((bool)jo["success"]);
var data = jo["data"];
Assert.IsTrue((bool)data["found"], "List<T> should resolve via generic normalization");
}
// ── generic types ───────────────────────────────────────────
[Test]
public void GetType_GenericList_Resolves()
{
var jo = Invoke("get_type", new JObject { ["class_name"] = "List<T>" });
Assert.IsTrue((bool)jo["success"]);
var data = jo["data"];
Assert.IsTrue((bool)data["found"], "List<T> should resolve via generic normalization");
}
[Test]
public void GetType_GenericDictionary_Resolves()
{
var jo = Invoke("get_type", new JObject { ["class_name"] = "Dictionary<TKey, TValue>" });
Assert.IsTrue((bool)jo["success"]);
var data = jo["data"];
Assert.IsTrue(
(bool)data["found"],
"Dictionary<TKey, TValue> should resolve via generic normalization with multiple type parameters"
);
}

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 introduces an opt-in “docs” tool group that helps assistants verify Unity APIs via live Editor reflection (unity_reflect) and fetch official Unity documentation (unity_docs), reducing incorrect/outdated API guidance.

Changes:

  • Added unity_reflect (Unity-side C# reflection tool + Python server wrapper + CLI command) with tests.
  • Added unity_docs (Python-only docs fetch/parse + asset-assisted lookup + CLI command) with tests.
  • Updated skill/docs/manifest/instructions to document and surface the new “docs” tool group.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
unity-mcp-skill/references/workflows.md Adds API verification workflow guidance for unity_reflect / unity_docs.
unity-mcp-skill/references/tools-reference.md Documents the new docs tools and parameters.
unity-mcp-skill/SKILL.md Adds “Docs” tool category entry to the skill overview table.
manifest.json Registers unity_docs and unity_reflect in the tool manifest list.
docs/i18n/README-zh.md Updates Chinese README tool list and recent updates to include the new tools.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/UnityReflectTests.cs.meta Adds Unity meta for the new EditMode test.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/UnityReflectTests.cs Adds Unity EditMode tests for the C# UnityReflect tool.
Server/tests/test_unity_reflect.py Adds Python unit tests for server-side unity_reflect tool behavior + group registration.
Server/tests/test_unity_docs.py Adds Python unit tests for docs URL building, parsing, and tool actions.
Server/src/services/tools/unity_reflect.py Implements server-side unity_reflect MCP tool routing to Unity.
Server/src/services/tools/unity_docs.py Implements Python-only unity_docs MCP tool (fetch/parse/lookup + optional asset search).
Server/src/services/registry/tool_registry.py Registers the new docs tool group.
Server/src/main.py Updates server instructions to nudge API verification when docs group is enabled.
Server/src/cli/main.py Registers new CLI subcommands: reflect and docs.
Server/src/cli/commands/reflect.py Adds CLI commands for reflect type/member/search.
Server/src/cli/commands/docs.py Adds CLI command to fetch ScriptReference docs (docs get).
README.md Updates English README tool list and recent updates to include new tools.
MCPForUnity/Editor/Tools/UnityReflect.cs.meta Adds Unity meta for the new C# tool.
MCPForUnity/Editor/Tools/UnityReflect.cs Implements Unity-side reflection tool (type/member/search + extension/obsolete detection).
.claude/skills/unity-mcp-skill/references/workflows.md Mirrors workflow doc updates for Claude skill copy.
.claude/skills/unity-mcp-skill/references/tools-reference.md Mirrors tool reference updates for Claude skill copy.
.claude/skills/unity-mcp-skill/SKILL.md Mirrors skill overview updates for Claude skill copy.

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

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +225 to +229
self._in_param_table = False

if tag == "div" and self._in_subsection:
self._in_subsection = False

seen_paths: set[str] = set()

async def _do_search(params: dict) -> list[dict]:
search_params: dict[str, Any] = {"action": "search", "path": ".", "pageSize": 10}
Comment on lines +550 to +552
except Exception:
pass # No Unity connection or import failure — skip silently
return None
Comment on lines +85 to +89
if query is not None:
params_dict["query"] = query
if action_lower == "search" and scope is not None:
params_dict["scope"] = scope

Use `unity_docs` `lookup` action to search multiple APIs in a single call:

```python
# Search ScriptReference + Manual + package docs in parallel
- **`get_doc`**: Fetch ScriptReference docs for a class or member. Parses HTML to extract description, signatures, parameters, return type, and code examples.
- **`get_manual`**: Fetch a Unity Manual page by slug. Returns title, sections, and code examples.
- **`get_package_doc`**: Fetch package documentation. Requires package name, page slug, and package version.
- **`lookup`**: Search all doc sources in parallel (ScriptReference + Manual + package docs). Supports batch queries. For asset-related queries (shader, material, texture, etc.), also searches project assets via `manage_asset`.
Comment on lines +626 to +636
private static string[] FindExtensionMethods(Type targetType)
{
lock (ExtensionMethodCache)
{
if (ExtensionMethodCache.TryGetValue(targetType, out var cached))
return cached;
}

var extensionNames = new HashSet<string>();
var cache = GetAssemblyTypeCache();

Use `unity_docs` `lookup` action to search multiple APIs in a single call:

```python
# Search ScriptReference + Manual + package docs in parallel
- **`get_doc`**: Fetch ScriptReference docs for a class or member. Parses HTML to extract description, signatures, parameters, return type, and code examples.
- **`get_manual`**: Fetch a Unity Manual page by slug. Returns title, sections, and code examples.
- **`get_package_doc`**: Fetch package documentation. Requires package name, page slug, and package version.
- **`lookup`**: Search all doc sources in parallel (ScriptReference + Manual + package docs). Supports batch queries. For asset-related queries (shader, material, texture, etc.), also searches project assets via `manage_asset`.
Comment on lines +190 to +200
self.signatures.append("".join(self._current_text).strip())

if tag == "div" and self._in_signature:
# Capture inline signature text (modern Unity docs don't use <pre>)
text = " ".join("".join(self._current_text).split()).strip()
# Remove "Declaration" prefix that appears inside the sig block
if text.startswith("Declaration"):
text = text[len("Declaration"):].strip()
if text:
self.signatures.append(text)
self._in_signature = False
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: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/skills/unity-mcp-skill/references/workflows.md:
- Around line 1906-1913: Add a clear opt-in prerequisite note above the "Full
API Verification Before Writing Code" section stating that the tools
`unity_reflect` and `unity_docs` must be enabled/installed (or the user must
join the opt-in group) before following the workflow; update the paragraph
before the header to mention users on fresh setups will need to opt in or enable
these tools to avoid "tool not found" errors, and reference the tool names
`unity_reflect` and `unity_docs` explicitly so readers know what to enable.

In `@manifest.json`:
- Around line 176-183: The manifest's published version was not updated after
adding new public tools (unity_docs, unity_reflect); update the "version" field
in manifest.json to a new appropriate semantic version (e.g., increment patch or
minor from 9.5.3) so published metadata matches the shipped capabilities,
ensuring the change is committed alongside the additions of unity_docs and
unity_reflect.

In `@MCPForUnity/Editor/Tools/UnityReflect.cs`:
- Around line 704-718: The current scope checks for "packages" and "project"
rely solely on assemblyName and miss asmdef/custom assemblies; update the logic
that contains the switch (the case "packages" and case "project" branches that
use assemblyName) to inspect the assembly's path/location in addition to its
name: determine the assembly file path (e.g., use Assembly.Location or the
caller-provided assemblyPath) and make "packages" return true when the path
contains "/Packages/" or the name follows package-style naming (contains a dot
like "com."), while excluding Unity and BCL prefixes
(assemblyName.StartsWith("Unity") / "System"/"mscorlib"/"netstandard"); make
"project" return true when the path contains "/Assets/" or
"/Library/ScriptAssemblies/" or when the assembly name matches project asmdef
patterns (non-Unity, non-BCL names including custom asmdef names and
Assembly-CSharp legacy names). Update the checks in the UnityReflect.cs switch
(the assemblyName variable and the "packages"/"project" cases) accordingly so
custom asmdef assemblies are classified correctly.
- Around line 247-252: get_member currently calls ResolveType() directly and
therefore accepts ambiguous short type names (e.g., "Button", "Image"); modify
get_member to mirror get_type behavior by rejecting unqualified short type names
before calling ResolveType: inspect classResult.Value (after
NormalizeGenericName), and if it does not contain a namespace qualifier (no '.'
or assembly-qualified marker) return an explicit error/ambiguous response rather
than falling back to namespace-prefix resolution; keep ResolveType usage for
fully-qualified names only and ensure the same ambiguity check is applied to any
code paths in get_member that use memberResult.Value.

In `@Server/src/cli/commands/docs.py`:
- Around line 6-8: The CLI command function get_doc is missing the required
`@handle_unity_errors` decorator; add `@handle_unity_errors` immediately above the
get_doc function definition to ensure Unity connection errors are handled
consistently, and add an import for handle_unity_errors (e.g., from
cli.utils.errors import handle_unity_errors) at the top of the file if it’s not
already imported; ensure the decorator is applied to the same function
referenced as get_doc so the docs get command follows the project CLI
error-handling guideline.

In `@Server/src/cli/commands/reflect.py`:
- Around line 27-30: The CLI currently prints whatever run_command returns (in
functions using get_config, run_command, and format_output) even when
run_command returns {"success": False,...}, causing failures to be treated as
successful exits; update each command handler (the blocks around run_command
calls at the shown ranges) to detect result.get("success") is False, output a
clear error (using click.echo or raising click.ClickException) with the error
details from the result, and ensure the process exits non‑zero (raise or
sys.exit(1)) instead of printing and returning 0 so handle_unity_errors behavior
and proper failure signaling are preserved.

In `@Server/src/services/tools/unity_docs.py`:
- Around line 184-200: The signature text is being appended twice for legacy
pages because a <pre> inside the signature block appends on </pre> and the
enclosing </div> appends the same content again; update the logic so the
div-closure handler (the block that checks self._in_signature and appends to
self.signatures) skips appending when the signature was already captured by the
inner </pre>. Concretely: when you handle the </pre> branch inside a signature
(where you currently set self._in_pre = False and append to self.signatures),
set a small sentinel (e.g., self._signature_from_pre = True), and in the
div-closure branch for self._in_signature only append if that sentinel is not
set; after handling the div, reset the sentinel and self._in_signature.
Reference symbols: _in_code_example, _in_pre, _current_text, _in_signature,
signatures, and add/clear the new _signature_from_pre flag.
- Around line 317-323: _flush_section currently only appends a section when
self._current_heading is set, so any intro text collected in self._content_parts
before the first heading is dropped; change _flush_section to also emit a
section when self._content_parts is non-empty even if self._current_heading is
None (e.g., append {"heading": self._current_heading or "", "content":
"\n".join(self._content_parts)}) and then clear self._current_heading and
self._content_parts so the page summary before the first heading is preserved;
see _flush_section, _current_heading, _content_parts, and sections for where to
apply this logic.

In `@Server/src/services/tools/unity_reflect.py`:
- Around line 27-29: The mcp_for_unity_tool decorator on the Unity Reflect tool
is using group="docs", which is not part of the allowed MCP groups; change the
group parameter on the mcp_for_unity_tool decorator (located above the Unity
Reflect tool definition) to one of the permitted values ('core', 'vfx',
'animation', 'ui', 'scripting_ext', 'probuilder', or 'testing') or, if the
intent is to introduce a new group, update the group policy/docs accordingly to
include "docs" and ensure manage_tools behavior is adjusted; update only the
decorator's group value or the group policy reference to resolve the conflict.
- Around line 87-88: Validate the incoming scope before adding it to
params_dict: check that when action_lower == "search" and scope is not None the
value of scope is one of the allowed constants in VALID_SCOPES (raise or return
an error / skip adding if invalid), and only then set params_dict["scope"] =
scope; update the code path in the function containing
action_lower/scope/params_dict (the block that currently uses VALID_SCOPES) to
enforce this server-side whitelist and handle invalid values explicitly (e.g.,
log and return a validation error or omit the parameter).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7da177a4-b0cb-4a6b-a3d0-501e91da48a5

📥 Commits

Reviewing files that changed from the base of the PR and between 44c925c and d91d0f1.

📒 Files selected for processing (22)
  • .claude/skills/unity-mcp-skill/SKILL.md
  • .claude/skills/unity-mcp-skill/references/tools-reference.md
  • .claude/skills/unity-mcp-skill/references/workflows.md
  • MCPForUnity/Editor/Tools/UnityReflect.cs
  • MCPForUnity/Editor/Tools/UnityReflect.cs.meta
  • README.md
  • Server/src/cli/commands/docs.py
  • Server/src/cli/commands/reflect.py
  • Server/src/cli/main.py
  • Server/src/main.py
  • Server/src/services/registry/tool_registry.py
  • Server/src/services/tools/unity_docs.py
  • Server/src/services/tools/unity_reflect.py
  • Server/tests/test_unity_docs.py
  • Server/tests/test_unity_reflect.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/UnityReflectTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/UnityReflectTests.cs.meta
  • docs/i18n/README-zh.md
  • manifest.json
  • unity-mcp-skill/SKILL.md
  • unity-mcp-skill/references/tools-reference.md
  • unity-mcp-skill/references/workflows.md

Comment on lines +176 to +183
{
"name": "unity_docs",
"description": "Fetch Unity documentation (ScriptReference, Manual, package docs)"
},
{
"name": "unity_reflect",
"description": "Inspect Unity C# APIs via live reflection"
},
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

Bump manifest version when adding new public tools.

This PR exposes new tools (unity_docs, unity_reflect), but manifest.json still reports 9.5.3 (Line 4). That can desync published metadata from shipped capability.

Suggested version bump
-  "version": "9.5.3",
+  "version": "9.5.4",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@manifest.json` around lines 176 - 183, The manifest's published version was
not updated after adding new public tools (unity_docs, unity_reflect); update
the "version" field in manifest.json to a new appropriate semantic version
(e.g., increment patch or minor from 9.5.3) so published metadata matches the
shipped capabilities, ensuring the change is committed alongside the additions
of unity_docs and unity_reflect.

Comment on lines +704 to +718
case "unity":
return assemblyName.StartsWith("UnityEngine")
|| assemblyName.StartsWith("UnityEditor")
|| assemblyName.StartsWith("Unity.");

case "packages":
return !assemblyName.StartsWith("System")
&& !assemblyName.StartsWith("mscorlib")
&& !assemblyName.StartsWith("netstandard");

case "project":
return assemblyName == "Assembly-CSharp"
|| assemblyName == "Assembly-CSharp-Editor"
|| assemblyName.StartsWith("Assembly-CSharp-firstpass")
|| assemblyName.StartsWith("Assembly-CSharp-Editor-firstpass");
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 | 🟠 Major

packages and project scope matching is not reliable in modern Unity projects.

packages currently admits every non-BCL assembly, so it includes UnityEngine*, UnityEditor*, and Assembly-CSharp*. project only matches the legacy Assembly-CSharp* names, so any custom asmdef disappears. That makes scope="packages" and scope="project" return misleading results.

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

In `@MCPForUnity/Editor/Tools/UnityReflect.cs` around lines 704 - 718, The current
scope checks for "packages" and "project" rely solely on assemblyName and miss
asmdef/custom assemblies; update the logic that contains the switch (the case
"packages" and case "project" branches that use assemblyName) to inspect the
assembly's path/location in addition to its name: determine the assembly file
path (e.g., use Assembly.Location or the caller-provided assemblyPath) and make
"packages" return true when the path contains "/Packages/" or the name follows
package-style naming (contains a dot like "com."), while excluding Unity and BCL
prefixes (assemblyName.StartsWith("Unity") / "System"/"mscorlib"/"netstandard");
make "project" return true when the path contains "/Assets/" or
"/Library/ScriptAssemblies/" or when the assembly name matches project asmdef
patterns (non-Unity, non-BCL names including custom asmdef names and
Assembly-CSharp legacy names). Update the checks in the UnityReflect.cs switch
(the assemblyName variable and the "packages"/"project" cases) accordingly so
custom asmdef assemblies are classified correctly.

Comment on lines +6 to +8
from cli.utils.config import get_config
from cli.utils.output import format_output

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 | 🟠 Major

Add @handle_unity_errors to the docs get command.

get_doc is a CLI command but currently skips the required decorator, so Unity connection failures won’t be handled consistently.

Suggested fix
 import asyncio
 import click
 
 from cli.utils.config import get_config
+from cli.utils.connection import handle_unity_errors
 from cli.utils.output import format_output
@@
 `@docs.command`("get")
 `@click.argument`("class_name")
 `@click.argument`("member_name", required=False)
 `@click.option`("--version", "-v", default=None, help="Unity version (e.g., 6000.0).")
+@handle_unity_errors
 def get_doc(class_name: str, member_name: str | None, version: str | None):

As per coding guidelines Server/src/cli/commands/*.py: “Python CLI commands must use the @handle_unity_errors decorator for error handling”.

Also applies to: 16-33

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

In `@Server/src/cli/commands/docs.py` around lines 6 - 8, The CLI command function
get_doc is missing the required `@handle_unity_errors` decorator; add
`@handle_unity_errors` immediately above the get_doc function definition to ensure
Unity connection errors are handled consistently, and add an import for
handle_unity_errors (e.g., from cli.utils.errors import handle_unity_errors) at
the top of the file if it’s not already imported; ensure the decorator is
applied to the same function referenced as get_doc so the docs get command
follows the project CLI error-handling guideline.

Comment on lines +27 to +30
config = get_config()
result = run_command("unity_reflect", {"action": "get_type", "class_name": class_name}, config)
click.echo(format_output(result, config.format))

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 | 🟠 Major

Structured tool failures are treated as successful CLI execution.

These handlers print the response directly even when run_command returns {"success": False, ...}. Because this is not an exception, @handle_unity_errors won’t trigger, and the command can exit 0 on failures.

Suggested fix
 import click
 
 from cli.utils.config import get_config
-from cli.utils.output import format_output
+from cli.utils.output import format_output, print_error
 from cli.utils.connection import run_command, handle_unity_errors
 
+def _echo_or_exit(result: dict, output_format: str) -> None:
+    if isinstance(result, dict) and result.get("success") is False:
+        print_error(result.get("message") or result.get("error") or "Unity command failed.")
+        raise SystemExit(1)
+    click.echo(format_output(result, output_format))
+
@@
     config = get_config()
     result = run_command("unity_reflect", {"action": "get_type", "class_name": class_name}, config)
-    click.echo(format_output(result, config.format))
+    _echo_or_exit(result, config.format)
@@
     result = run_command("unity_reflect", {
         "action": "get_member",
         "class_name": class_name,
         "member_name": member_name,
     }, config)
-    click.echo(format_output(result, config.format))
+    _echo_or_exit(result, config.format)
@@
     result = run_command("unity_reflect", {
         "action": "search",
         "query": query,
         "scope": scope,
     }, config)
-    click.echo(format_output(result, config.format))
+    _echo_or_exit(result, config.format)

Also applies to: 44-50, 66-72

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

In `@Server/src/cli/commands/reflect.py` around lines 27 - 30, The CLI currently
prints whatever run_command returns (in functions using get_config, run_command,
and format_output) even when run_command returns {"success": False,...}, causing
failures to be treated as successful exits; update each command handler (the
blocks around run_command calls at the shown ranges) to detect
result.get("success") is False, output a clear error (using click.echo or
raising click.ClickException) with the error details from the result, and ensure
the process exits non‑zero (raise or sys.exit(1)) instead of printing and
returning 0 so handle_unity_errors behavior and proper failure signaling are
preserved.

Comment on lines +27 to +29
@mcp_for_unity_tool(
group="docs",
description=(
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 | 🟠 Major

group="docs" conflicts with the current allowed MCP group set.

This tool registers with a group outside the documented allowed list. Please align to an allowed group or update the group policy/docs in tandem.

As per coding guidelines: “MCP tool group parameter must be set to one of: 'core' (default, enabled), 'vfx', 'animation', 'ui', 'scripting_ext', 'probuilder', or 'testing' (disabled by default, toggled via manage_tools)”.

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

In `@Server/src/services/tools/unity_reflect.py` around lines 27 - 29, The
mcp_for_unity_tool decorator on the Unity Reflect tool is using group="docs",
which is not part of the allowed MCP groups; change the group parameter on the
mcp_for_unity_tool decorator (located above the Unity Reflect tool definition)
to one of the permitted values ('core', 'vfx', 'animation', 'ui',
'scripting_ext', 'probuilder', or 'testing') or, if the intent is to introduce a
new group, update the group policy/docs accordingly to include "docs" and ensure
manage_tools behavior is adjusted; update only the decorator's group value or
the group policy reference to resolve the conflict.

- Fix lock ordering deadlock: switch ExtensionMethodCache to ConcurrentDictionary
- Add ambiguity check to get_member (was missing, get_type already had it)
- Add Dictionary<TKey, TValue> generic resolution test
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: 3

♻️ Duplicate comments (1)
MCPForUnity/Editor/Tools/UnityReflect.cs (1)

707-725: ⚠️ Potential issue | 🟠 Major

packages/project scope still misclassifies modern asmdef assemblies.

packages currently accepts every non-BCL assembly, so it also pulls in Unity* and project assemblies, while project only recognizes the legacy Assembly-CSharp* names. In asmdef-based projects that makes both scopes misleading. Please classify from assembly location (/Packages/ vs /Assets/ or /Library/ScriptAssemblies/) instead of assembly name alone.

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

In `@MCPForUnity/Editor/Tools/UnityReflect.cs` around lines 707 - 725,
MatchesScope currently classifies assemblies by name; update it to inspect the
assembly file path (assembly.Location or assembly.CodeBase) instead when
handling "packages" and "project". In the MatchesScope method (and any callers
using an assemblyName string), accept or obtain the assembly's Location and for
scope "packages" return true when the path contains "/Packages/" (and still
exclude BCL prefixes like "System", "mscorlib", "netstandard"); for scope
"project" return true when the path contains "/Assets/" or
"/Library/ScriptAssemblies/" (or when the assemblyName matches legacy
Assembly-CSharp* names as a fallback). Also handle null/empty Location safely by
falling back to the original name-based checks so existing behavior is
preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@MCPForUnity/Editor/Tools/UnityReflect.cs`:
- Around line 278-357: The get_member path fails to resolve extension methods
because it only inspects instance/static members on type; add a fallback before
the final "not found" return that searches for static extension methods whose
first parameter matches the target type (use flags and look for methods marked
with System.Runtime.CompilerServices.ExtensionAttribute or
MethodInfo.IsDefined(typeof(ExtensionAttribute), false)), e.g. iterate candidate
assemblies/types (or reuse the same source used to populate extension_methods in
get_type) to find methods named memberName where the first parameter's type
matches or is assignable to the inspected type, format overloads with
FormatMethodDetail, and return a SuccessResponse with member_type =
"extension_method", overload_count and overloads and declare the extension
method's declaring_type via FormatTypeName so extension names returned by
get_type are resolved here.

In `@Server/src/services/tools/unity_docs.py`:
- Around line 613-621: The current loop in lookup that zips labels and results
discards responses where result is an Exception or result.get("success") is
False, turning real fetch errors into silent "not found" hits; update the logic
in the loop that builds hits to also collect per-source errors (e.g., build an
errors list with entries like {"source": label, "error": result} for Exception
or {"source": label, "error": result} when success is False) and after the loop,
if hits is empty but errors is non-empty, return or raise a failure indicating
all sources errored (or include the errors in the lookup response payload) so
callers can distinguish real errors from an empty-not-found result; keep use of
labels and results and preserve existing success/data handling for actual found
hits.
- Around line 600-607: The code currently lowercases query into manual_slug and
only tries that form which can miss mixed-case slugs; change it to first try the
original-spelling slug (replace spaces/underscores with hyphens but keep case)
when calling _get_manual and _get_package_doc, then add a fallback task that
tries the lowercased variant only if it differs. Specifically, keep the
normalized hyphenized slug (e.g., original_slug = query.replace(" ",
"-").replace("_","-")), append tasks.append(("manual",
_get_manual(original_slug, version))), and if original_slug.lower() !=
original_slug append the lowercase fallback tasks.append(("manual_fallback",
_get_manual(original_slug.lower(), version))) and mirror this for the package
page variable used with _get_package_doc when package and pkg_version are
provided.

---

Duplicate comments:
In `@MCPForUnity/Editor/Tools/UnityReflect.cs`:
- Around line 707-725: MatchesScope currently classifies assemblies by name;
update it to inspect the assembly file path (assembly.Location or
assembly.CodeBase) instead when handling "packages" and "project". In the
MatchesScope method (and any callers using an assemblyName string), accept or
obtain the assembly's Location and for scope "packages" return true when the
path contains "/Packages/" (and still exclude BCL prefixes like "System",
"mscorlib", "netstandard"); for scope "project" return true when the path
contains "/Assets/" or "/Library/ScriptAssemblies/" (or when the assemblyName
matches legacy Assembly-CSharp* names as a fallback). Also handle null/empty
Location safely by falling back to the original name-based checks so existing
behavior is preserved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 519a804c-d7a4-4653-8cf9-8a75d73fd077

📥 Commits

Reviewing files that changed from the base of the PR and between d91d0f1 and 3d704c3.

📒 Files selected for processing (6)
  • MCPForUnity/Editor/Tools/UnityReflect.cs
  • Server/src/services/tools/unity_docs.py
  • Server/src/services/tools/unity_reflect.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/UnityReflectTests.cs
  • unity-mcp-skill/references/tools-reference.md
  • unity-mcp-skill/references/workflows.md
✅ Files skipped from review due to trivial changes (1)
  • unity-mcp-skill/references/workflows.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • unity-mcp-skill/references/tools-reference.md

- get_member now resolves extension methods as fallback before "not found"
- lookup preserves original-case slugs for Manual pages (UIE-USS-Properties-Reference)
- lookup surfaces per-source errors instead of silently dropping them
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 (5)
MCPForUnity/Editor/Tools/UnityReflect.cs (2)

685-690: Minor: Redundant type matching conditions.

IsAssignableFrom already returns true for exact type matches and subclass relationships, making the IsSubclassOf and == checks redundant. This doesn't affect correctness but adds slight overhead.

♻️ Simplify type matching
                        var paramType = firstParam.ParameterType;
-                        if (paramType.IsAssignableFrom(targetType) || targetType.IsSubclassOf(paramType)
-                            || paramType == targetType
-                            || (paramType.IsGenericType && IsGenericMatch(paramType, targetType)))
+                        if (paramType.IsAssignableFrom(targetType)
+                            || (paramType.IsGenericType && IsGenericMatch(paramType, targetType)))
                        {
                            extensionNames.Add(method.Name);
                        }

The same simplification applies to FindExtensionMethodInfos at lines 728-730.

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

In `@MCPForUnity/Editor/Tools/UnityReflect.cs` around lines 685 - 690, The
type-matching condition is redundant: remove the explicit paramType ==
targetType and targetType.IsSubclassOf(paramType) checks and rely on
paramType.IsAssignableFrom(targetType) plus the existing generic fallback
IsGenericMatch(paramType, targetType); update the block that currently adds
method.Name to extensionNames (using paramType, targetType, IsGenericMatch) to
only test IsAssignableFrom || (paramType.IsGenericType && IsGenericMatch(...));
make the same simplification inside FindExtensionMethodInfos so both places use
the streamlined logic.

86-93: Consider logging swallowed exceptions in debug builds.

The empty catch block is understandable for handling dynamic assemblies that throw on GetExportedTypes(), but silently dropping all exceptions can mask unexpected issues during development.

♻️ Optional: Add debug logging
                 try
                 {
                     _assemblyTypeCache[asm.FullName] = asm.GetExportedTypes();
                 }
-                catch
+                catch (Exception ex)
                 {
-                    // Some assemblies throw on GetExportedTypes
+                    // Some assemblies (dynamic, reflection-only) throw on GetExportedTypes
+#if UNITY_EDITOR && DEBUG
+                    UnityEngine.Debug.LogWarning($"[UnityReflect] Skipping assembly {asm.GetName().Name}: {ex.GetType().Name}");
+#endif
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Tools/UnityReflect.cs` around lines 86 - 93, The empty
catch around asm.GetExportedTypes() swallows all exceptions; change it to catch
Exception ex and, in debug/editor builds, log the exception (e.g., via
UnityEngine.Debug.LogException or Debug.LogWarning) along with contextual info
such as asm.FullName and the _assemblyTypeCache key so developers can see
unexpected failures during development while still ignoring known problematic
assemblies in release builds.
Server/src/services/tools/unity_docs.py (3)

91-100: Consider validating URL scheme for defense in depth.

While all current callers construct URLs with hardcoded https://docs.unity3d.com bases, urlopen accepts arbitrary schemes including file://. Adding a scheme check would provide defense against future misuse.

🛡️ Optional hardening
     def _do_fetch() -> tuple[int, str, str]:
+        if not url.startswith(("https://", "http://")):
+            raise ValueError(f"Invalid URL scheme: {url}")
         req = Request(url, headers={"User-Agent": "MCPForUnity/1.0"})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Server/src/services/tools/unity_docs.py` around lines 91 - 100, Add a scheme
check at the top of _do_fetch: parse url with urllib.parse.urlparse and ensure
parsed.scheme == "https" (or a whitelist if you want to allow "http"); if the
scheme is not allowed, raise a suitable exception (e.g., ValueError or URLError)
before calling Request/urlopen so file:// or other schemes cannot be used.
Update the imports if needed and keep existing HTTPError/URLError handling
intact.

618-632: Past issues addressed; minor style note on zip().

Error surfacing (lines 619-631) and original-case slug handling (lines 600-606) are now correctly implemented per past review feedback.

Line 621: While labels and results lengths are inherently equal (both derived from tasks), adding strict=True to zip() is a Python best practice that documents this invariant.

♻️ Optional: Add strict=True to zip
-    for label, result in zip(labels, results):
+    for label, result in zip(labels, results, strict=True):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Server/src/services/tools/unity_docs.py` around lines 618 - 632, The loop
that pairs labels and results should use zip(labels, results, strict=True) to
assert the lists are the same length; update the for loop that currently reads
for label, result in zip(labels, results): to include strict=True so mismatched
lengths raise an error, leaving the handling of hits, errors, and the subsequent
isinstance/result checks unchanged (references: labels, results, hits, errors).

562-570: Broad exception handling is acceptable but consider logging.

The defensive exception handling is appropriate since asset search is an optional enhancement. However, silently swallowing the exception when ctx.warning() fails (lines 568-569) loses diagnostic information. Consider logging at debug level if a logger is available.

🔍 Optional: Add debug logging for swallowed exceptions
         except Exception as e:
             try:
                 if hasattr(ctx, 'warning'):
                     await ctx.warning(f"Asset search failed: {e}")
             except Exception:
-                pass  # ctx might not be usable
+                pass  # ctx might not be usable; asset search is optional

Given the defensive nature of this code (asset search is optional), the current approach is acceptable.

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

In `@Server/src/services/tools/unity_docs.py` around lines 562 - 570, In the
except block that handles failures calling ctx.warning (inside the asset search
handling in unity_docs.py), preserve diagnostic info by logging the swallowed
exception at debug level: if a module or passed-in logger is available (e.g.,
logger), call logger.debug("Asset search ctx.warning failed", exc_info=True) or
similar; otherwise fallback to printing to stderr. Ensure you reference the
caught exception variable e and the ctx.warning call when adding the debug log
so the diagnostic is recorded without changing the existing control flow.
🤖 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/unity_docs.py`:
- Around line 660-668: The code silently drops exceptions from asyncio.gather
results so callers never see batch-level failures; update the post-gather
handling (the block that iterates over results and builds query_results) to
collect exceptions into an errors list (e.g., gather_errors) and include them in
the returned response alongside query_results and found count; specifically,
when iterating over results from asyncio.gather in the caller of _lookup_single,
push Exception instances into gather_errors (preserving the exception
message/trace) instead of just continuing, and ensure the final return payload
includes that errors list so callers can see why queries failed.

---

Nitpick comments:
In `@MCPForUnity/Editor/Tools/UnityReflect.cs`:
- Around line 685-690: The type-matching condition is redundant: remove the
explicit paramType == targetType and targetType.IsSubclassOf(paramType) checks
and rely on paramType.IsAssignableFrom(targetType) plus the existing generic
fallback IsGenericMatch(paramType, targetType); update the block that currently
adds method.Name to extensionNames (using paramType, targetType, IsGenericMatch)
to only test IsAssignableFrom || (paramType.IsGenericType &&
IsGenericMatch(...)); make the same simplification inside
FindExtensionMethodInfos so both places use the streamlined logic.
- Around line 86-93: The empty catch around asm.GetExportedTypes() swallows all
exceptions; change it to catch Exception ex and, in debug/editor builds, log the
exception (e.g., via UnityEngine.Debug.LogException or Debug.LogWarning) along
with contextual info such as asm.FullName and the _assemblyTypeCache key so
developers can see unexpected failures during development while still ignoring
known problematic assemblies in release builds.

In `@Server/src/services/tools/unity_docs.py`:
- Around line 91-100: Add a scheme check at the top of _do_fetch: parse url with
urllib.parse.urlparse and ensure parsed.scheme == "https" (or a whitelist if you
want to allow "http"); if the scheme is not allowed, raise a suitable exception
(e.g., ValueError or URLError) before calling Request/urlopen so file:// or
other schemes cannot be used. Update the imports if needed and keep existing
HTTPError/URLError handling intact.
- Around line 618-632: The loop that pairs labels and results should use
zip(labels, results, strict=True) to assert the lists are the same length;
update the for loop that currently reads for label, result in zip(labels,
results): to include strict=True so mismatched lengths raise an error, leaving
the handling of hits, errors, and the subsequent isinstance/result checks
unchanged (references: labels, results, hits, errors).
- Around line 562-570: In the except block that handles failures calling
ctx.warning (inside the asset search handling in unity_docs.py), preserve
diagnostic info by logging the swallowed exception at debug level: if a module
or passed-in logger is available (e.g., logger), call logger.debug("Asset search
ctx.warning failed", exc_info=True) or similar; otherwise fallback to printing
to stderr. Ensure you reference the caught exception variable e and the
ctx.warning call when adding the debug log so the diagnostic is recorded without
changing the existing control flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff4359e6-c7e0-4f96-8109-6471fda1dd23

📥 Commits

Reviewing files that changed from the base of the PR and between 3d704c3 and 6a8c774.

📒 Files selected for processing (2)
  • MCPForUnity/Editor/Tools/UnityReflect.cs
  • Server/src/services/tools/unity_docs.py

Comment on lines +660 to +668
results = await asyncio.gather(*tasks, return_exceptions=True)

query_results = []
for result in results:
if isinstance(result, Exception):
continue
if isinstance(result, dict):
query_results.append(result)

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

Exception results from batch queries are silently discarded.

When _lookup_single throws an exception, it's silently skipped (lines 664-665). Unlike the error handling in _lookup_single (which collects errors per-source), batch-level exceptions don't surface to the caller. If all queries fail with exceptions, the response shows found: 0 with no indication of why.

🐛 Suggested fix to track failed queries
     query_results = []
+    failed_queries = []
     for result in results:
         if isinstance(result, Exception):
+            failed_queries.append({"error": str(result)})
             continue
         if isinstance(result, dict):
             query_results.append(result)
+
+    # Include failed queries in summary if any
+    summary = {
+        "total": len(queries),
+        "found": len(all_found),
+        "missed": len(all_missed),
+    }
+    if failed_queries:
+        summary["failed"] = len(failed_queries)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Server/src/services/tools/unity_docs.py` around lines 660 - 668, The code
silently drops exceptions from asyncio.gather results so callers never see
batch-level failures; update the post-gather handling (the block that iterates
over results and builds query_results) to collect exceptions into an errors list
(e.g., gather_errors) and include them in the returned response alongside
query_results and found count; specifically, when iterating over results from
asyncio.gather in the caller of _lookup_single, push Exception instances into
gather_errors (preserving the exception message/trace) instead of just
continuing, and ensure the final return payload includes that errors list so
callers can see why queries failed.

@Scriptwonder Scriptwonder merged commit 66a0016 into CoplayDev:beta Mar 16, 2026
2 checks passed
@Scriptwonder Scriptwonder deleted the feat/unity-api-verification-tools branch March 16, 2026 17:23
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.

2 participants