@@ -14,6 +14,16 @@ namespace MCPForUnity.Editor.Services
1414 [ InitializeOnLoad ]
1515 internal static class HttpBridgeReloadHandler
1616 {
17+ private static readonly TimeSpan [ ] ResumeRetrySchedule =
18+ {
19+ TimeSpan . Zero ,
20+ TimeSpan . FromSeconds ( 1 ) ,
21+ TimeSpan . FromSeconds ( 3 ) ,
22+ TimeSpan . FromSeconds ( 5 ) ,
23+ TimeSpan . FromSeconds ( 10 ) ,
24+ TimeSpan . FromSeconds ( 30 )
25+ } ;
26+
1727 static HttpBridgeReloadHandler ( )
1828 {
1929 AssemblyReloadEvents . beforeAssemblyReload += OnBeforeAssemblyReload ;
@@ -38,14 +48,9 @@ private static void OnBeforeAssemblyReload()
3848
3949 if ( shouldResume )
4050 {
41- var stopTask = transport . StopAsync ( TransportMode . Http ) ;
42- stopTask . ContinueWith ( t =>
43- {
44- if ( t . IsFaulted && t . Exception != null )
45- {
46- McpLog . Warn ( $ "Error stopping MCP bridge before reload: { t . Exception . GetBaseException ( ) . Message } ") ;
47- }
48- } , TaskScheduler . Default ) ;
51+ // beforeAssemblyReload is synchronous; force a synchronous teardown so we do not
52+ // leave an orphaned socket due to an unfinished async close handshake.
53+ transport . ForceStop ( TransportMode . Http ) ;
4954 }
5055 }
5156 catch ( Exception ex )
@@ -90,56 +95,69 @@ private static void OnAfterAssemblyReload()
9095
9196 if ( ! isCompiling )
9297 {
93- try
98+ _ = ResumeHttpWithRetriesAsync ( ) ;
99+ return ;
100+ }
101+
102+ // Fallback when compiling: schedule on the editor loop
103+ EditorApplication . delayCall += ( ) =>
104+ {
105+ _ = ResumeHttpWithRetriesAsync ( ) ;
106+ } ;
107+ }
108+
109+ private static async Task ResumeHttpWithRetriesAsync ( )
110+ {
111+ Exception lastException = null ;
112+
113+ for ( int i = 0 ; i < ResumeRetrySchedule . Length ; i ++ )
114+ {
115+ int attempt = i + 1 ;
116+ McpLog . Debug ( $ "[HTTP Reload] Resume attempt { attempt } /{ ResumeRetrySchedule . Length } ") ;
117+
118+ TimeSpan delay = ResumeRetrySchedule [ i ] ;
119+ if ( delay > TimeSpan . Zero )
94120 {
95- var startTask = MCPServiceLocator . TransportManager . StartAsync ( TransportMode . Http ) ;
96- startTask . ContinueWith ( t =>
97- {
98- if ( t . IsFaulted )
99- {
100- var baseEx = t . Exception ? . GetBaseException ( ) ;
101- McpLog . Warn ( $ "Failed to resume HTTP MCP bridge after domain reload: { baseEx ? . Message } ") ;
102- return ;
103- }
104- bool started = t . Result ;
105- if ( ! started )
106- {
107- McpLog . Warn ( "Failed to resume HTTP MCP bridge after domain reload" ) ;
108- }
109- else
110- {
111- MCPForUnityEditorWindow . RequestHealthVerification ( ) ;
112- }
113- } , TaskScheduler . Default ) ;
114- return ;
121+ McpLog . Debug ( $ "[HTTP Reload] Waiting { delay . TotalSeconds : 0.#} s before resume attempt { attempt } ") ;
122+ try { await Task . Delay ( delay ) ; }
123+ catch { return ; }
115124 }
116- catch ( Exception ex )
125+
126+ // Abort retries if the user switched transports while we were waiting.
127+ if ( ! EditorConfigurationCache . Instance . UseHttpTransport )
117128 {
118- McpLog . Error ( $ "Error resuming HTTP MCP bridge: { ex . Message } ") ;
119129 return ;
120130 }
121- }
122131
123- // Fallback when compiling: schedule on the editor loop
124- EditorApplication . delayCall += async ( ) =>
125- {
126132 try
127133 {
128134 bool started = await MCPServiceLocator . TransportManager . StartAsync ( TransportMode . Http ) ;
129- if ( ! started )
130- {
131- McpLog . Warn ( "Failed to resume HTTP MCP bridge after domain reload" ) ;
132- }
133- else
135+ if ( started )
134136 {
137+ McpLog . Debug ( $ "[HTTP Reload] Resume succeeded on attempt { attempt } ") ;
135138 MCPForUnityEditorWindow . RequestHealthVerification ( ) ;
139+ return ;
136140 }
141+
142+ var state = MCPServiceLocator . TransportManager . GetState ( TransportMode . Http ) ;
143+ string reason = string . IsNullOrWhiteSpace ( state ? . Error ) ? "no error detail" : state . Error ;
144+ McpLog . Debug ( $ "[HTTP Reload] Resume attempt { attempt } failed: { reason } ") ;
137145 }
138146 catch ( Exception ex )
139147 {
140- McpLog . Error ( $ "Error resuming HTTP MCP bridge: { ex . Message } ") ;
148+ lastException = ex ;
149+ McpLog . Debug ( $ "[HTTP Reload] Resume attempt { attempt } threw: { ex . Message } ") ;
141150 }
142- } ;
151+ }
152+
153+ if ( lastException != null )
154+ {
155+ McpLog . Warn ( $ "Failed to resume HTTP MCP bridge after domain reload: { lastException . Message } ") ;
156+ }
157+ else
158+ {
159+ McpLog . Warn ( "Failed to resume HTTP MCP bridge after domain reload" ) ;
160+ }
143161 }
144162 }
145163}
0 commit comments