From c76d0bb281f802dbf83a1e1caa077c6ce9f48da2 Mon Sep 17 00:00:00 2001 From: Shutong Wu <51266340+Scriptwonder@users.noreply.github.com> Date: Thu, 2 Apr 2026 23:23:07 -0400 Subject: [PATCH 1/3] Fix on #837 --- .../Editor/Resources/Editor/ToolStates.cs | 26 +- .../Transports/StdioTransportClient.cs | 4 +- Server/src/services/custom_tool_service.py | 3 + Server/src/services/tools/__init__.py | 67 +++++ .../src/transport/legacy/unity_connection.py | 47 ++- .../test_stdio_custom_tool_sync.py | 270 ++++++++++++++++++ 6 files changed, 413 insertions(+), 4 deletions(-) create mode 100644 Server/tests/integration/test_stdio_custom_tool_sync.py diff --git a/MCPForUnity/Editor/Resources/Editor/ToolStates.cs b/MCPForUnity/Editor/Resources/Editor/ToolStates.cs index 9ca626121..8d60a899a 100644 --- a/MCPForUnity/Editor/Resources/Editor/ToolStates.cs +++ b/MCPForUnity/Editor/Resources/Editor/ToolStates.cs @@ -23,11 +23,35 @@ public static object HandleCommand(JObject @params) var toolsArray = new JArray(); foreach (var tool in allTools) { + var paramsArray = new JArray(); + if (tool.Parameters != null) + { + foreach (var p in tool.Parameters) + { + paramsArray.Add(new JObject + { + ["name"] = p.Name, + ["description"] = p.Description, + ["type"] = p.Type, + ["required"] = p.Required, + ["default_value"] = p.DefaultValue + }); + } + } + toolsArray.Add(new JObject { ["name"] = tool.Name, ["group"] = tool.Group ?? "core", - ["enabled"] = discovery.IsToolEnabled(tool.Name) + ["enabled"] = discovery.IsToolEnabled(tool.Name), + ["description"] = tool.Description, + ["auto_register"] = tool.AutoRegister, + ["is_built_in"] = tool.IsBuiltIn, + ["structured_output"] = tool.StructuredOutput, + ["requires_polling"] = tool.RequiresPolling, + ["poll_action"] = tool.PollAction, + ["max_poll_seconds"] = tool.MaxPollSeconds, + ["parameters"] = paramsArray }); } diff --git a/MCPForUnity/Editor/Services/Transport/Transports/StdioTransportClient.cs b/MCPForUnity/Editor/Services/Transport/Transports/StdioTransportClient.cs index 9dadc4336..cf07087f1 100644 --- a/MCPForUnity/Editor/Services/Transport/Transports/StdioTransportClient.cs +++ b/MCPForUnity/Editor/Services/Transport/Transports/StdioTransportClient.cs @@ -48,8 +48,8 @@ public Task VerifyAsync() public Task ReregisterToolsAsync() { - // Stdio transport doesn't support dynamic tool reregistration - // Tools are registered at server startup + // In stdio mode, Python re-syncs tools automatically on reconnection + // after domain reload. No proactive push mechanism exists over TCP. return Task.CompletedTask; } diff --git a/Server/src/services/custom_tool_service.py b/Server/src/services/custom_tool_service.py index 7407d9a9d..33d182569 100644 --- a/Server/src/services/custom_tool_service.py +++ b/Server/src/services/custom_tool_service.py @@ -118,6 +118,9 @@ async def get_tool_definition( user_id: str | None = None, ) -> ToolDefinitionModel | None: tool = self._project_tools.get(project_id, {}).get(tool_name) + if tool: + return tool + tool = self._global_tools.get(tool_name) if tool: return tool return await PluginHub.get_tool_definition(project_id, tool_name, user_id=user_id) diff --git a/Server/src/services/tools/__init__.py b/Server/src/services/tools/__init__.py index 858afd141..6f2f7e95d 100644 --- a/Server/src/services/tools/__init__.py +++ b/Server/src/services/tools/__init__.py @@ -169,6 +169,72 @@ async def sync_tool_visibility_from_unity( PluginHub._sync_server_tool_visibility(enabled_tools) + # Register custom (non-built-in) tools via CustomToolService. + # The extended get_tool_states response includes is_built_in, + # description, parameters, etc. If those fields are missing + # (older Unity package), we skip custom tool registration. + custom_tool_count = 0 + has_extended_metadata = any( + "is_built_in" in t for t in enabled_tools + ) + if has_extended_metadata: + custom_tool_dicts = [ + t for t in enabled_tools if not t.get("is_built_in", True) + ] + if custom_tool_dicts: + try: + from models.models import ToolDefinitionModel, ToolParameterModel + from services.custom_tool_service import CustomToolService + + custom_tool_models = [] + for td in custom_tool_dicts: + params = [ + ToolParameterModel( + name=p.get("name", ""), + description=p.get("description", ""), + type=p.get("type", "string"), + required=p.get("required", False), + default_value=p.get("default_value"), + ) + for p in td.get("parameters", []) + ] + custom_tool_models.append( + ToolDefinitionModel( + name=td["name"], + description=td.get("description", ""), + structured_output=td.get("structured_output", True), + requires_polling=td.get("requires_polling", False), + poll_action=td.get("poll_action", "status"), + max_poll_seconds=td.get("max_poll_seconds", 0), + parameters=params, + ) + ) + + service = CustomToolService.get_instance() + service.register_global_tools(custom_tool_models) + custom_tool_count = len(custom_tool_models) + logger.info( + "Registered %d custom tool(s) from Unity via stdio sync", + custom_tool_count, + ) + except RuntimeError as exc: + logger.debug( + "Skipping custom tool registration: " + "CustomToolService not initialized yet (%s)", + exc, + ) + except Exception as exc: + logger.warning( + "Failed to register custom tools from Unity: %s", + exc, + ) + else: + logger.debug( + "Unity response does not include extended tool metadata " + "(is_built_in); skipping custom tool registration. " + "Update MCPForUnity to enable custom tool sync in stdio mode." + ) + if notify: await PluginHub._notify_mcp_tool_list_changed() @@ -191,6 +257,7 @@ async def sync_tool_visibility_from_unity( "disabled_groups": disabled_groups, "enabled_tool_count": len(enabled_tools), "total_tool_count": len(tools), + "custom_tool_count": custom_tool_count, } except Exception as exc: diff --git a/Server/src/transport/legacy/unity_connection.py b/Server/src/transport/legacy/unity_connection.py index c62e20fb1..86201806e 100644 --- a/Server/src/transport/legacy/unity_connection.py +++ b/Server/src/transport/legacy/unity_connection.py @@ -42,6 +42,7 @@ def __post_init__(self): self.port = stdio_port_registry.get_port(self.instance_id) self._io_lock = threading.Lock() self._conn_lock = threading.Lock() + self._needs_tool_resync = False # Set True after reconnection def _prepare_socket(self, sock: socket.socket) -> None: try: @@ -65,6 +66,7 @@ def connect(self) -> bool: self.sock = socket.create_connection( (self.host, self.port), connect_timeout) self._prepare_socket(self.sock) + self._needs_tool_resync = True logger.debug(f"Connected to Unity at {self.host}:{self.port}") # Strict handshake: require FRAMING=1 @@ -933,11 +935,54 @@ async def async_send_command_with_retry( import asyncio # local import to avoid mandatory asyncio dependency for sync callers if loop is None: loop = asyncio.get_running_loop() - return await loop.run_in_executor( + result = await loop.run_in_executor( None, lambda: send_command_with_retry( command_type, params, instance_id=instance_id, max_retries=max_retries, retry_ms=retry_ms, retry_on_reload=retry_on_reload), ) + + # After a successful command, check if the connection was freshly + # established (reconnection after domain reload). If so, re-sync + # tool visibility and custom tool registration from Unity. + # Skip if *this* call is the sync itself to avoid recursion. + if command_type != "get_tool_states": + try: + pool = get_unity_connection_pool() + conn = pool.get_connection(instance_id) + if getattr(conn, "_needs_tool_resync", False): + conn._needs_tool_resync = False + logger.info( + "Detected reconnection to Unity; scheduling tool re-sync" + ) + asyncio.ensure_future(_resync_tools_after_reconnect(instance_id)) + except Exception: + pass # Best-effort; don't fail the actual command + + return result except Exception as e: return MCPResponse(success=False, error=str(e)) + + +async def _resync_tools_after_reconnect(instance_id: str | None) -> None: + """Background task: re-sync tool visibility and custom tools after reconnection.""" + try: + from services.tools import sync_tool_visibility_from_unity + result = await sync_tool_visibility_from_unity( + instance_id=instance_id, notify=True, + ) + if result.get("synced"): + logger.info( + "Post-reconnection tool re-sync complete: " + "enabled=[%s], disabled=[%s], custom_tools=%d", + ", ".join(result.get("enabled_groups", [])), + ", ".join(result.get("disabled_groups", [])), + result.get("custom_tool_count", 0), + ) + else: + logger.debug( + "Post-reconnection tool re-sync skipped: %s", + result.get("error", "unknown"), + ) + except Exception as exc: + logger.debug("Post-reconnection tool re-sync failed: %s", exc) diff --git a/Server/tests/integration/test_stdio_custom_tool_sync.py b/Server/tests/integration/test_stdio_custom_tool_sync.py new file mode 100644 index 000000000..522d901b4 --- /dev/null +++ b/Server/tests/integration/test_stdio_custom_tool_sync.py @@ -0,0 +1,270 @@ +""" +Tests for stdio-mode custom tool discovery (GitHub issue #837). + +Verifies that: +1. sync_tool_visibility_from_unity registers custom tools when extended metadata is present +2. Custom tools are skipped gracefully when metadata is missing (old Unity package) +3. Reconnection flag triggers a background re-sync +""" +import asyncio +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _make_unity_response(tools, include_extended=True): + """Build a fake get_tool_states response from Unity.""" + tool_list = [] + for t in tools: + entry = { + "name": t["name"], + "group": t.get("group", "core"), + "enabled": t.get("enabled", True), + } + if include_extended: + entry.update({ + "description": t.get("description", f"Tool: {t['name']}"), + "auto_register": t.get("auto_register", True), + "is_built_in": t.get("is_built_in", True), + "structured_output": t.get("structured_output", False), + "requires_polling": t.get("requires_polling", False), + "poll_action": t.get("poll_action", "status"), + "max_poll_seconds": t.get("max_poll_seconds", 0), + "parameters": t.get("parameters", []), + }) + tool_list.append(entry) + return { + "data": { + "tools": tool_list, + "groups": [], + } + } + + +BUILTIN_TOOL = { + "name": "manage_gameobject", + "group": "core", + "is_built_in": True, + "description": "Manage GameObjects in the scene.", +} + +CUSTOM_TOOL = { + "name": "test_ping", + "group": "core", + "is_built_in": False, + "description": "Simple test tool that returns a pong.", + "parameters": [ + {"name": "message", "description": "Message to echo", "type": "string", "required": False, "default_value": "pong"}, + ], +} + + +# --------------------------------------------------------------------------- +# sync_tool_visibility_from_unity — custom tool registration +# --------------------------------------------------------------------------- + +@pytest.mark.asyncio +async def test_sync_registers_custom_tools(): + """Custom (non-built-in) tools should be registered via CustomToolService.""" + response = _make_unity_response([BUILTIN_TOOL, CUSTOM_TOOL]) + + mock_service = MagicMock() + mock_service.register_global_tools = MagicMock() + + with patch( + "transport.legacy.unity_connection.async_send_command_with_retry", + new_callable=AsyncMock, + return_value=response, + ), patch( + "transport.plugin_hub.PluginHub._sync_server_tool_visibility", + ), patch( + "transport.plugin_hub.PluginHub._notify_mcp_tool_list_changed", + new_callable=AsyncMock, + ), patch( + "services.custom_tool_service.CustomToolService.get_instance", + return_value=mock_service, + ): + from services.tools import sync_tool_visibility_from_unity + result = await sync_tool_visibility_from_unity(notify=False) + + assert result["synced"] is True + assert result["custom_tool_count"] == 1 + + # Verify register_global_tools was called with the custom tool + mock_service.register_global_tools.assert_called_once() + registered = mock_service.register_global_tools.call_args[0][0] + assert len(registered) == 1 + assert registered[0].name == "test_ping" + assert registered[0].description == "Simple test tool that returns a pong." + assert len(registered[0].parameters) == 1 + assert registered[0].parameters[0].name == "message" + + +@pytest.mark.asyncio +async def test_sync_skips_builtin_tools(): + """Built-in tools should NOT be passed to register_global_tools.""" + response = _make_unity_response([BUILTIN_TOOL]) + + with patch( + "transport.legacy.unity_connection.async_send_command_with_retry", + new_callable=AsyncMock, + return_value=response, + ), patch( + "transport.plugin_hub.PluginHub._sync_server_tool_visibility", + ), patch( + "transport.plugin_hub.PluginHub._notify_mcp_tool_list_changed", + new_callable=AsyncMock, + ), patch( + "services.custom_tool_service.CustomToolService.get_instance", + ) as mock_get_instance: + from services.tools import sync_tool_visibility_from_unity + result = await sync_tool_visibility_from_unity(notify=False) + + assert result["synced"] is True + assert result["custom_tool_count"] == 0 + # No custom tools → register_global_tools should NOT be called + mock_get_instance.assert_not_called() + + +@pytest.mark.asyncio +async def test_sync_skips_when_no_extended_metadata(): + """When Unity returns old-format data (no is_built_in), skip custom tool registration.""" + response = _make_unity_response([BUILTIN_TOOL, CUSTOM_TOOL], include_extended=False) + + with patch( + "transport.legacy.unity_connection.async_send_command_with_retry", + new_callable=AsyncMock, + return_value=response, + ), patch( + "transport.plugin_hub.PluginHub._sync_server_tool_visibility", + ), patch( + "transport.plugin_hub.PluginHub._notify_mcp_tool_list_changed", + new_callable=AsyncMock, + ), patch( + "services.custom_tool_service.CustomToolService.get_instance", + ) as mock_get_instance: + from services.tools import sync_tool_visibility_from_unity + result = await sync_tool_visibility_from_unity(notify=False) + + assert result["synced"] is True + assert result["custom_tool_count"] == 0 + mock_get_instance.assert_not_called() + + +@pytest.mark.asyncio +async def test_sync_handles_custom_tool_service_not_initialized(): + """If CustomToolService isn't initialized yet, skip gracefully (no crash).""" + response = _make_unity_response([CUSTOM_TOOL]) + + with patch( + "transport.legacy.unity_connection.async_send_command_with_retry", + new_callable=AsyncMock, + return_value=response, + ), patch( + "transport.plugin_hub.PluginHub._sync_server_tool_visibility", + ), patch( + "transport.plugin_hub.PluginHub._notify_mcp_tool_list_changed", + new_callable=AsyncMock, + ), patch( + "services.custom_tool_service.CustomToolService.get_instance", + side_effect=RuntimeError("not initialized"), + ): + from services.tools import sync_tool_visibility_from_unity + result = await sync_tool_visibility_from_unity(notify=False) + + # Should succeed overall even though custom tool registration failed + assert result["synced"] is True + assert result["custom_tool_count"] == 0 + + +# --------------------------------------------------------------------------- +# Reconnection re-sync trigger +# --------------------------------------------------------------------------- + +@pytest.mark.asyncio +async def test_reconnection_flag_triggers_resync(): + """After reconnection, async_send_command_with_retry should schedule a re-sync.""" + mock_conn = MagicMock() + mock_conn._needs_tool_resync = True + mock_conn.instance_id = None + + mock_pool = MagicMock() + mock_pool.get_connection.return_value = mock_conn + + with patch( + "transport.legacy.unity_connection.send_command_with_retry", + return_value={"success": True, "message": "ok"}, + ), patch( + "transport.legacy.unity_connection.get_unity_connection_pool", + return_value=mock_pool, + ), patch( + "transport.legacy.unity_connection._resync_tools_after_reconnect", + new_callable=AsyncMock, + ) as mock_resync: + from transport.legacy.unity_connection import async_send_command_with_retry + result = await async_send_command_with_retry("manage_gameobject", {"action": "list"}) + + # ensure_future schedules on the event loop; give it a tick to run + await asyncio.sleep(0) + + assert result["success"] is True + # Flag should be cleared + assert mock_conn._needs_tool_resync is False + # Re-sync should have been scheduled + mock_resync.assert_awaited_once_with(None) + + +@pytest.mark.asyncio +async def test_no_resync_for_get_tool_states(): + """get_tool_states itself should NOT trigger re-sync (avoids recursion).""" + mock_conn = MagicMock() + mock_conn._needs_tool_resync = True + + mock_pool = MagicMock() + mock_pool.get_connection.return_value = mock_conn + + with patch( + "transport.legacy.unity_connection.send_command_with_retry", + return_value={"data": {"tools": []}}, + ), patch( + "transport.legacy.unity_connection.get_unity_connection_pool", + return_value=mock_pool, + ), patch( + "transport.legacy.unity_connection._resync_tools_after_reconnect", + new_callable=AsyncMock, + ) as mock_resync: + from transport.legacy.unity_connection import async_send_command_with_retry + await async_send_command_with_retry("get_tool_states", {}) + + # Flag should NOT be cleared — get_tool_states is excluded + assert mock_conn._needs_tool_resync is True + mock_resync.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_no_resync_when_not_reconnected(): + """When _needs_tool_resync is False, no re-sync should be scheduled.""" + mock_conn = MagicMock() + mock_conn._needs_tool_resync = False + + mock_pool = MagicMock() + mock_pool.get_connection.return_value = mock_conn + + with patch( + "transport.legacy.unity_connection.send_command_with_retry", + return_value={"success": True}, + ), patch( + "transport.legacy.unity_connection.get_unity_connection_pool", + return_value=mock_pool, + ), patch( + "transport.legacy.unity_connection._resync_tools_after_reconnect", + new_callable=AsyncMock, + ) as mock_resync: + from transport.legacy.unity_connection import async_send_command_with_retry + await async_send_command_with_retry("manage_gameobject", {"action": "list"}) + + mock_resync.assert_not_awaited() From af37bccc3d4f1ffcddbc6c70a4955ecede7acd3d Mon Sep 17 00:00:00 2001 From: Shutong Wu <51266340+Scriptwonder@users.noreply.github.com> Date: Thu, 2 Apr 2026 23:49:08 -0400 Subject: [PATCH 2/3] Fix --- .../Editor/Resources/Editor/ToolStates.cs | 2 +- .../Transports/WebSocketTransportClient.cs | 2 +- Server/src/services/tools/__init__.py | 4 ++-- .../src/transport/legacy/unity_connection.py | 22 +++++++++++-------- .../test_stdio_custom_tool_sync.py | 4 ++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/MCPForUnity/Editor/Resources/Editor/ToolStates.cs b/MCPForUnity/Editor/Resources/Editor/ToolStates.cs index 8d60a899a..192e76002 100644 --- a/MCPForUnity/Editor/Resources/Editor/ToolStates.cs +++ b/MCPForUnity/Editor/Resources/Editor/ToolStates.cs @@ -49,7 +49,7 @@ public static object HandleCommand(JObject @params) ["is_built_in"] = tool.IsBuiltIn, ["structured_output"] = tool.StructuredOutput, ["requires_polling"] = tool.RequiresPolling, - ["poll_action"] = tool.PollAction, + ["poll_action"] = tool.PollAction ?? "status", ["max_poll_seconds"] = tool.MaxPollSeconds, ["parameters"] = paramsArray }); diff --git a/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs b/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs index fd276609c..8aaf2249e 100644 --- a/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs +++ b/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs @@ -540,7 +540,7 @@ private async Task SendRegisterToolsAsync(CancellationToken token) ["description"] = tool.Description, ["structured_output"] = tool.StructuredOutput, ["requires_polling"] = tool.RequiresPolling, - ["poll_action"] = tool.PollAction, + ["poll_action"] = tool.PollAction ?? "status", ["max_poll_seconds"] = tool.MaxPollSeconds, ["group"] = string.IsNullOrWhiteSpace(tool.Group) ? "core" : tool.Group }; diff --git a/Server/src/services/tools/__init__.py b/Server/src/services/tools/__init__.py index 6f2f7e95d..b587663d2 100644 --- a/Server/src/services/tools/__init__.py +++ b/Server/src/services/tools/__init__.py @@ -193,7 +193,7 @@ async def sync_tool_visibility_from_unity( name=p.get("name", ""), description=p.get("description", ""), type=p.get("type", "string"), - required=p.get("required", False), + required=p.get("required", True), default_value=p.get("default_value"), ) for p in td.get("parameters", []) @@ -204,7 +204,7 @@ async def sync_tool_visibility_from_unity( description=td.get("description", ""), structured_output=td.get("structured_output", True), requires_polling=td.get("requires_polling", False), - poll_action=td.get("poll_action", "status"), + poll_action=td.get("poll_action") or "status", max_poll_seconds=td.get("max_poll_seconds", 0), parameters=params, ) diff --git a/Server/src/transport/legacy/unity_connection.py b/Server/src/transport/legacy/unity_connection.py index 86201806e..1d703a862 100644 --- a/Server/src/transport/legacy/unity_connection.py +++ b/Server/src/transport/legacy/unity_connection.py @@ -945,19 +945,23 @@ async def async_send_command_with_retry( # After a successful command, check if the connection was freshly # established (reconnection after domain reload). If so, re-sync # tool visibility and custom tool registration from Unity. - # Skip if *this* call is the sync itself to avoid recursion. - if command_type != "get_tool_states": - try: - pool = get_unity_connection_pool() - conn = pool.get_connection(instance_id) - if getattr(conn, "_needs_tool_resync", False): - conn._needs_tool_resync = False + # Always clear the flag, but only schedule the background resync + # when this call is not itself get_tool_states (to avoid recursion). + try: + pool = get_unity_connection_pool() + conn = pool.get_connection(instance_id) + if getattr(conn, "_needs_tool_resync", False): + conn._needs_tool_resync = False + if command_type != "get_tool_states": logger.info( "Detected reconnection to Unity; scheduling tool re-sync" ) asyncio.ensure_future(_resync_tools_after_reconnect(instance_id)) - except Exception: - pass # Best-effort; don't fail the actual command + except Exception as exc: + logger.debug( + "Failed to schedule post-reconnection tool re-sync: %s", + exc, + ) return result except Exception as e: diff --git a/Server/tests/integration/test_stdio_custom_tool_sync.py b/Server/tests/integration/test_stdio_custom_tool_sync.py index 522d901b4..d7dd35765 100644 --- a/Server/tests/integration/test_stdio_custom_tool_sync.py +++ b/Server/tests/integration/test_stdio_custom_tool_sync.py @@ -240,8 +240,8 @@ async def test_no_resync_for_get_tool_states(): from transport.legacy.unity_connection import async_send_command_with_retry await async_send_command_with_retry("get_tool_states", {}) - # Flag should NOT be cleared — get_tool_states is excluded - assert mock_conn._needs_tool_resync is True + # Flag should be cleared, but no re-sync task should be scheduled + assert mock_conn._needs_tool_resync is False mock_resync.assert_not_awaited() From f476d68d820eefd232e493545acdeeab9bd75278 Mon Sep 17 00:00:00 2001 From: Shutong Wu <51266340+Scriptwonder@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:22:47 -0400 Subject: [PATCH 3/3] Update on issue#1020 --- MCPForUnity/Editor/Helpers/Response.cs.meta | 2 +- MCPForUnity/Editor/MCPForUnity.Editor.asmdef | 3 +-- MCPForUnity/Runtime/MCPForUnity.Runtime.asmdef | 4 +--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/MCPForUnity/Editor/Helpers/Response.cs.meta b/MCPForUnity/Editor/Helpers/Response.cs.meta index 6fd11e39b..349444af4 100644 --- a/MCPForUnity/Editor/Helpers/Response.cs.meta +++ b/MCPForUnity/Editor/Helpers/Response.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 80c09a76b944f8c4691e06c4d76c4be8 +guid: e8a93c1537f546c6b3b24b903d5f9395 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/MCPForUnity/Editor/MCPForUnity.Editor.asmdef b/MCPForUnity/Editor/MCPForUnity.Editor.asmdef index 96850293d..7e991cbbf 100644 --- a/MCPForUnity/Editor/MCPForUnity.Editor.asmdef +++ b/MCPForUnity/Editor/MCPForUnity.Editor.asmdef @@ -2,8 +2,7 @@ "name": "MCPForUnity.Editor", "rootNamespace": "MCPForUnity.Editor", "references": [ - "MCPForUnity.Runtime", - "Newtonsoft.Json" + "MCPForUnity.Runtime" ], "includePlatforms": [ "Editor" diff --git a/MCPForUnity/Runtime/MCPForUnity.Runtime.asmdef b/MCPForUnity/Runtime/MCPForUnity.Runtime.asmdef index 857872de1..c5c19611b 100644 --- a/MCPForUnity/Runtime/MCPForUnity.Runtime.asmdef +++ b/MCPForUnity/Runtime/MCPForUnity.Runtime.asmdef @@ -1,9 +1,7 @@ { "name": "MCPForUnity.Runtime", "rootNamespace": "MCPForUnity.Runtime", - "references": [ - "Newtonsoft.Json" - ], + "references": [], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false,