@@ -42,6 +42,8 @@ public final class MCPService {
4242
4343 private var transport : HTTPSSETransport ?
4444
45+ private var transportTask : Task < Void , Never > ?
46+
4547 private var startTask : Task < Void , Never > ?
4648
4749 private var observeToken : ObserveToken ?
@@ -61,7 +63,11 @@ public final class MCPService {
6163 private init ( ) {
6264 let appSupportURL = FileManager . default. urls ( for: . applicationSupportDirectory, in: . userDomainMask) . first!
6365 let runtimeViewerDir = appSupportURL. appendingPathComponent ( " RuntimeViewer " )
64- try ? FileManager . default. createDirectory ( at: runtimeViewerDir, withIntermediateDirectories: true )
66+ do {
67+ try FileManager . default. createDirectory ( at: runtimeViewerDir, withIntermediateDirectories: true )
68+ } catch {
69+ logger. error ( " Failed to create app support directory: \( error) " )
70+ }
6571 self . portFilePath = runtimeViewerDir. appendingPathComponent ( Settings . MCP. portFileName) . path
6672 }
6773
@@ -70,9 +76,17 @@ public final class MCPService {
7076 }
7177
7278 public func start( for documentProvider: some MCPBridgeDocumentProvider ) {
73- let mcpSettings = settings. mcp
74- guard mcpSettings. isEnabled else {
79+ self . documentProvider = documentProvider
80+
81+ // Initialize previous values before observing to avoid a spurious restart
82+ let currentMCP = settings. mcp
83+ previousMCPEnabled = currentMCP. isEnabled
84+ previousMCPUsesFixedPort = currentMCP. useFixedPort
85+ previousMCPFixedPort = currentMCP. fixedPort
86+
87+ guard currentMCP. isEnabled else {
7588 serverState = . disabled
89+ observe ( )
7690 return
7791 }
7892 startTask = Task {
@@ -82,10 +96,9 @@ public final class MCPService {
8296 let mcpServer = MCPBridgeServer ( documentProvider: documentProvider)
8397 let transport = HTTPSSETransport ( server: mcpServer, host: " 127.0.0.1 " , port: Int ( port) )
8498 self . transport = transport
85- self . documentProvider = documentProvider
8699
87100 // Run transport in a detached task (run() blocks on the NIO event loop)
88- Task . detached {
101+ self . transportTask = Task . detached {
89102 do {
90103 try await transport. run ( )
91104 } catch {
@@ -103,11 +116,6 @@ public final class MCPService {
103116 logger. error ( " Failed to start MCP server: \( error) " )
104117 self . serverState = . stopped
105118 }
106- // Initialize previous values before observing to avoid a spurious restart
107- let currentMCP = settings. mcp
108- previousMCPEnabled = currentMCP. isEnabled
109- previousMCPUsesFixedPort = currentMCP. useFixedPort
110- previousMCPFixedPort = currentMCP. fixedPort
111119 observe ( )
112120 }
113121 }
@@ -117,6 +125,13 @@ public final class MCPService {
117125 startTask = nil
118126 restartTask? . cancel ( )
119127 restartTask = nil
128+ transportTask? . cancel ( )
129+ transportTask = nil
130+ if let transport {
131+ Task . detached {
132+ try ? await transport. stop ( )
133+ }
134+ }
120135 transport = nil
121136 let isEnabled = settings. mcp. isEnabled
122137 serverState = isEnabled ? . stopped : . disabled
@@ -178,6 +193,12 @@ public final class MCPService {
178193 }
179194
180195 private nonisolated func removePortFile( ) {
181- try ? FileManager . default. removeItem ( atPath: portFilePath)
196+ do {
197+ try FileManager . default. removeItem ( atPath: portFilePath)
198+ } catch let error as NSError where error. domain == NSCocoaErrorDomain && error. code == NSFileNoSuchFileError {
199+ // File already removed, ignore
200+ } catch {
201+ logger. error ( " Failed to remove port file: \( error) " )
202+ }
182203 }
183204}
0 commit comments