Skip to content

update for resources-bug-fix#884

Merged
Scriptwonder merged 1 commit into
CoplayDev:betafrom
Scriptwonder:resource-bug-fix
Mar 7, 2026
Merged

update for resources-bug-fix#884
Scriptwonder merged 1 commit into
CoplayDev:betafrom
Scriptwonder:resource-bug-fix

Conversation

@Scriptwonder

@Scriptwonder Scriptwonder commented Mar 7, 2026

Copy link
Copy Markdown
Collaborator

Fix the resources bug that emerged when updating FastMCP to V3. It does not accept Pydantic BaseModel anyway so it is now wrapped in JSON format. GUI updates that update the version number when using advanced deployment are also included.

Summary by Sourcery

Wrap MCP resource handlers to serialize Pydantic and dict responses for FastMCP v3 compatibility and update the Unity editor UI to refresh version info after advanced deployments.

Bug Fixes:

  • Ensure resource functions returning Pydantic models or dicts are serialized to JSON so FastMCP 3.x accepts their outputs.
  • Trigger Unity editor version label and update checks after package deploy and restore actions in the advanced deployment section.

Enhancements:

  • Introduce a reusable serialization wrapper for MCP resource registration that can be layered with existing logging and telemetry decorators.

Summary by CodeRabbit

  • New Features

    • Package deployment now automatically triggers version label updates and queues an update validation check in the editor interface.
  • Bug Fixes

    • Enhanced resource serialization handling to ensure proper JSON conversion and compatibility with FastMCP 3.x for various result types.

Copilot AI review requested due to automatic review settings March 7, 2026 07:12
@sourcery-ai

sourcery-ai Bot commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a serialization wrapper so resource handlers returning Pydantic models or dicts are converted to JSON strings for FastMCP v3, and wires Unity editor advanced deployment events to refresh the displayed version and trigger update checks after deploy/restore operations.

Sequence diagram for FastMCP resource serialization wrapper

sequenceDiagram
    participant Client
    participant FastMCP
    participant ResourceFunction as Resource_function
    participant SerializeWrapper as _serialize_pydantic_wrapper

    Client->>FastMCP: HTTP request to resource URI
    FastMCP->>SerializeWrapper: Invoke wrapped resource handler
    SerializeWrapper->>ResourceFunction: Call original resource function
    ResourceFunction-->>SerializeWrapper: MCPResponse(BaseModel) or dict or str
    alt Result is BaseModel
        SerializeWrapper->>SerializeWrapper: result.model_dump_json()
        SerializeWrapper-->>FastMCP: JSON string
    else Result is dict
        SerializeWrapper->>SerializeWrapper: json.dumps(result)
        SerializeWrapper-->>FastMCP: JSON string
    else Result is already str/bytes/ResourceResult
        SerializeWrapper-->>FastMCP: result
    end
    FastMCP-->>Client: Resource response (JSON string or allowed type)
Loading

Sequence diagram for Unity advanced deployment and version refresh

sequenceDiagram
    actor User
    participant McpAdvancedSection
    participant MCPForUnityEditorWindow

    User->>McpAdvancedSection: Click Deploy or Restore
    McpAdvancedSection->>McpAdvancedSection: Perform deployment or restore
    McpAdvancedSection->>User: Show dialog Deployment Complete or Restore Complete
    McpAdvancedSection->>McpAdvancedSection: OnPackageDeployed?.Invoke()
    McpAdvancedSection-->>MCPForUnityEditorWindow: OnPackageDeployed event
    MCPForUnityEditorWindow->>MCPForUnityEditorWindow: UpdateVersionLabel()
    MCPForUnityEditorWindow->>MCPForUnityEditorWindow: QueueUpdateCheck()
Loading

Class diagram for resource registration and Unity advanced deployment events

classDiagram
    class ResourcesModule {
        +register_all_resources(mcp, project_scoped_tools)
        +_serialize_pydantic(func)
    }

    class FastMCP {
        +resource(uri, name, description, handler)
    }

    class BaseModel {
        +model_dump_json()
    }

    ResourcesModule ..> FastMCP : uses
    ResourcesModule ..> BaseModel : checks_instance_of

    class MCPForUnityEditorWindow {
        +CreateGUI()
        -UpdateVersionLabel()
        -QueueUpdateCheck()
    }

    class McpAdvancedSection {
        +event OnPackageDeployed
        +Root
        -OnDeployClicked()
        -OnRestoreBackupClicked()
    }

    class ConnectionSection {
        +VerifyBridgeConnectionAsync()
        +SetHealthStatusUpdateCallback(callback)
    }

    MCPForUnityEditorWindow o-- McpAdvancedSection : contains
    MCPForUnityEditorWindow o-- ConnectionSection : contains
    McpAdvancedSection ..> MCPForUnityEditorWindow : OnPackageDeployed event
    ConnectionSection ..> McpAdvancedSection : health_status_callback
Loading

File-Level Changes

Change Details Files
Ensure all registered resources return FastMCP v3-compatible JSON strings instead of raw Pydantic models or dicts.
  • Introduce a _serialize_pydantic async wrapper that converts BaseModel results to JSON via model_dump_json and dict results via json.dumps before returning.
  • Wrap discovered resource functions with _serialize_pydantic prior to existing log_execution, telemetry_resource, and mcp.resource decorators for both URI templates with and without query parameters.
Server/src/services/resources/__init__.py
Update the Unity editor advanced deployment workflow to raise a deployment-complete event and refresh version information in the main editor window.
  • Add an OnPackageDeployed event to McpAdvancedSection and invoke it after successful deploy and restore-backup operations.
  • Subscribe to OnPackageDeployed from MCPForUnityEditorWindow to call UpdateVersionLabel and QueueUpdateCheck whenever advanced deployment completes.
MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs

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

coderabbitai Bot commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

The pull request introduces an event-driven update mechanism in the MCPForUnity editor window and adds a serialization wrapper for Pydantic model handling in the server resource services. A new OnPackageDeployed event is raised after successful package deployment, triggering UI version label updates and update checks. Additionally, a _serialize_pydantic decorator converts Pydantic models to JSON strings before resource processing.

Changes

Cohort / File(s) Summary
MCPForUnity Event System
MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs, MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs
Added OnPackageDeployed event that fires after deployment completion. Window now subscribes to this event to refresh version label display and queue update validation checks.
Server Serialization
Server/src/services/resources/__init__.py
Introduced _serialize_pydantic decorator wrapper that converts Pydantic BaseModel instances and dicts to JSON strings before logging and telemetry processing. Applied to resource functions in both parameterized and non-parameterized URI code paths.

Sequence Diagram(s)

sequenceDiagram
    participant McpAdv as McpAdvancedSection
    participant Event as OnPackageDeployed Event
    participant Window as MCPForUnityEditorWindow
    participant UI as Version Label & Update Check

    McpAdv->>Event: Raise OnPackageDeployed
    Event->>Window: Notify subscribers
    Window->>UI: Update version label
    Window->>UI: Queue update check
Loading
sequenceDiagram
    participant Client as Resource Function
    participant Wrapper as _serialize_pydantic
    participant Model as Pydantic BaseModel
    participant JSON as JSON Serializer
    participant FastMCP as FastMCP 3.x

    Client->>Wrapper: Call with result
    alt Result is BaseModel
        Wrapper->>Model: Detect BaseModel type
        Model->>JSON: model_dump_json()
        JSON->>Wrapper: JSON string
    else Result is dict
        Wrapper->>JSON: json.dumps()
        JSON->>Wrapper: JSON string
    else Other
        Wrapper->>Wrapper: Return as-is
    end
    Wrapper->>FastMCP: Pass serialized result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A package hops and deploys with pride,
Events cascade like clover far and wide,
Pydantic models dance to JSON's tune,
While labels bloom and updates bloom soon,
In FastMCP's garden, all things align,
New features sprout—a system so fine! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 'update for resources-bug-fix' is vague and generic, using non-descriptive phrasing that doesn't clearly convey what the changeset accomplishes. Use a more specific title like 'Fix FastMCP v3 compatibility with Pydantic serialization and update deployment UI' to better summarize the main changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the core changes and includes bug fixes and enhancements, but lacks specific details in the 'Changes Made' section and is missing formal documentation update checklist completion.

✏️ 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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The _serialize_pydantic wrapper only handles a single BaseModel or dict; if any existing resources return lists or nested structures of Pydantic models, you may want to extend this helper to recursively serialize common container types (e.g., list/tuple/dict of models) to avoid subtle runtime issues.
  • By placing _serialize_pydantic as the outermost wrapper before log_execution and telemetry_resource, those middle layers now see only JSON strings rather than structured models; consider whether these wrappers should instead operate on the original BaseModel (e.g., for richer logging/metrics) and apply serialization only at the final FastMCP boundary.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `_serialize_pydantic` wrapper only handles a single `BaseModel` or `dict`; if any existing resources return lists or nested structures of Pydantic models, you may want to extend this helper to recursively serialize common container types (e.g., list/tuple/dict of models) to avoid subtle runtime issues.
- By placing `_serialize_pydantic` as the outermost wrapper before `log_execution` and `telemetry_resource`, those middle layers now see only JSON strings rather than structured models; consider whether these wrappers should instead operate on the original `BaseModel` (e.g., for richer logging/metrics) and apply serialization only at the final FastMCP boundary.

## Individual Comments

### Comment 1
<location path="MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs" line_range="47-50" />
<code_context>
         public event Action OnGitUrlChanged;
         public event Action OnHttpServerCommandUpdateRequested;
         public event Action OnTestConnectionRequested;
+        public event Action OnPackageDeployed;

         public VisualElement Root { get; private set; }
</code_context>
<issue_to_address>
**suggestion:** Event name may be misleading since it also fires on restore.

Since this event is raised for both deploy and restore, the name doesn’t reflect its full behavior. Consider renaming it to something more generic (e.g., `OnPackageChanged`) or adding distinct events for deploy and restore so subscribers can clearly distinguish the operation.

Suggested implementation:

```csharp
        public event Action OnGitUrlChanged;
        public event Action OnHttpServerCommandUpdateRequested;
        public event Action OnTestConnectionRequested;

        /// <summary>
        /// Raised whenever the package deployment state changes (deploy or restore).
        /// </summary>
        public event Action OnPackageChanged;

        /// <summary>
        /// Raised specifically after a successful package deployment.
        /// </summary>
        public event Action OnPackageDeployed;

        /// <summary>
        /// Raised specifically after a successful package restore.
        /// </summary>
        public event Action OnPackageRestored;

```

```csharp
            else
            {
                EditorUtility.DisplayDialog(
                    "Deployment Complete",
                    result.Message + (string.IsNullOrEmpty(result.BackupPath) ? string.Empty : $"\nBackup: {result.BackupPath}"),
                    "OK");

                // Notify listeners of a successful deployment and a package state change.
                OnPackageDeployed?.Invoke();
                OnPackageChanged?.Invoke();
            }

```

To fully implement the intent of your review comment, you should also:
1. Locate the code path that performs a **restore** operation in `McpAdvancedSection` (or related classes in this editor window) and:
   - Replace any existing `OnPackageDeployed?.Invoke()` call that is used for restore with:
     - `OnPackageRestored?.Invoke();`
     - `OnPackageChanged?.Invoke();`
2. Update any external subscribers:
   - If any code currently relies on `OnPackageDeployed` to be fired for restore, adjust those subscribers to:
     - Listen to `OnPackageChanged` when they don’t care which operation occurred, **or**
     - Listen to `OnPackageDeployed` and/or `OnPackageRestored` when they need to distinguish between deploy and restore.
This will ensure the event naming is no longer misleading while giving callers explicit hooks for both operations.
</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 +47 to 50
public event Action OnPackageDeployed;

public VisualElement Root { get; private set; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: Event name may be misleading since it also fires on restore.

Since this event is raised for both deploy and restore, the name doesn’t reflect its full behavior. Consider renaming it to something more generic (e.g., OnPackageChanged) or adding distinct events for deploy and restore so subscribers can clearly distinguish the operation.

Suggested implementation:

        public event Action OnGitUrlChanged;
        public event Action OnHttpServerCommandUpdateRequested;
        public event Action OnTestConnectionRequested;

        /// <summary>
        /// Raised whenever the package deployment state changes (deploy or restore).
        /// </summary>
        public event Action OnPackageChanged;

        /// <summary>
        /// Raised specifically after a successful package deployment.
        /// </summary>
        public event Action OnPackageDeployed;

        /// <summary>
        /// Raised specifically after a successful package restore.
        /// </summary>
        public event Action OnPackageRestored;
            else
            {
                EditorUtility.DisplayDialog(
                    "Deployment Complete",
                    result.Message + (string.IsNullOrEmpty(result.BackupPath) ? string.Empty : $"\nBackup: {result.BackupPath}"),
                    "OK");

                // Notify listeners of a successful deployment and a package state change.
                OnPackageDeployed?.Invoke();
                OnPackageChanged?.Invoke();
            }

To fully implement the intent of your review comment, you should also:

  1. Locate the code path that performs a restore operation in McpAdvancedSection (or related classes in this editor window) and:
    • Replace any existing OnPackageDeployed?.Invoke() call that is used for restore with:
      • OnPackageRestored?.Invoke();
      • OnPackageChanged?.Invoke();
  2. Update any external subscribers:
    • If any code currently relies on OnPackageDeployed to be fired for restore, adjust those subscribers to:
      • Listen to OnPackageChanged when they don’t care which operation occurred, or
      • Listen to OnPackageDeployed and/or OnPackageRestored when they need to distinguish between deploy and restore.
        This will ensure the event naming is no longer misleading while giving callers explicit hooks for both operations.

@Scriptwonder Scriptwonder merged commit 735afc1 into CoplayDev:beta Mar 7, 2026
4 of 5 checks passed
@Scriptwonder Scriptwonder deleted the resource-bug-fix branch March 7, 2026 07:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a bug introduced when upgrading to FastMCP v3, which stopped accepting Pydantic BaseModel instances as resource return values. It wraps all resource handler functions with a _serialize_pydantic decorator that converts BaseModel and dict returns to JSON strings. Additionally, the Unity editor GUI is updated to refresh the version label and trigger an update check after successful advanced deployment or backup restore operations.

Changes:

  • Added _serialize_pydantic wrapper in the resource registration pipeline to serialize Pydantic/dict responses to JSON strings for FastMCP v3 compatibility
  • Added OnPackageDeployed event to McpAdvancedSection and raised it after both successful deploy and backup restore actions
  • Wired the event in MCPForUnityEditorWindow to call UpdateVersionLabel() and QueueUpdateCheck() after package changes

Reviewed changes

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

File Description
Server/src/services/resources/__init__.py Adds _serialize_pydantic decorator and applies it as the innermost wrapper before log_execution and telemetry_resource in resource registration
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs Subscribes to the new OnPackageDeployed event to refresh version info after package changes
MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.cs Adds OnPackageDeployed event and raises it on successful deploy and restore operations
Server/uv.lock Version bump from 9.4.8 to 9.5.1, consistent with pyproject.toml

💡 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.

if isinstance(result, BaseModel):
return result.model_dump_json()
if isinstance(result, dict):
import json

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

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

The import json statement is placed inside the hot path of the wrapper function, within the isinstance(result, dict) branch. While Python caches module imports in sys.modules so re-importing is cheap, it still incurs a dictionary lookup on every call that returns a dict. The json module should be moved to the top-level imports alongside the other standard library imports (e.g., next to import functools) for consistency with the rest of the codebase and to avoid any per-call overhead.

Copilot uses AI. Check for mistakes.
else
{
EditorUtility.DisplayDialog("Restore Complete", result.Message, "OK");
OnPackageDeployed?.Invoke();

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

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

The OnPackageDeployed event is fired from OnRestoreBackupClicked when a backup restore succeeds. While the behavior (refreshing version info) is correct and intentional, the event name OnPackageDeployed is semantically misleading for a restore operation - restoring is not deploying. Consider renaming the event to something more general like OnPackageInstallationChanged to accurately describe both deploy and restore scenarios.

Copilot uses AI. Check for mistakes.
@Scriptwonder Scriptwonder restored the resource-bug-fix branch March 8, 2026 18:07
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