From 622f4bbb2282101c95fefad1b14e25f8ed910619 Mon Sep 17 00:00:00 2001 From: Adam Van Prooyen Date: Tue, 17 Mar 2026 23:27:53 -0700 Subject: [PATCH] Fix SIGTRAP crash from continuation misuse in connection handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MCP SDK's NetworkTransport has reconnection logic that creates unstructured tasks holding CheckedContinuation references. For server-side incoming connections, this reconnection path can cause double-resume when connections are cancelled, crashing the app. Disable reconnection and heartbeats for server-accepted connections since they are client features — if a client disconnects, it should reconnect itself. Also make removeConnection idempotent to prevent stop() being called twice on the same connection. Co-Authored-By: Claude Opus 4.6 (1M context) --- App/Controllers/ServerController.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/App/Controllers/ServerController.swift b/App/Controllers/ServerController.swift index 255c2bb6..2f2a9632 100644 --- a/App/Controllers/ServerController.swift +++ b/App/Controllers/ServerController.swift @@ -432,6 +432,8 @@ actor MCPConnectionManager { self.transport = NetworkTransport( connection: connection, logger: nil, + heartbeatConfig: .init(enabled: false), + reconnectionConfig: .disabled, bufferConfig: .unlimited ) @@ -627,6 +629,7 @@ actor ServerNetworkManager { private var connections: [UUID: MCPConnectionManager] = [:] private var connectionTasks: [UUID: Task] = [:] private var pendingConnections: [UUID: String] = [:] + private var removedConnections: Set = [] typealias ConnectionApprovalHandler = @Sendable (UUID, MCP.Client.Info) async -> Bool private var connectionApprovalHandler: ConnectionApprovalHandler? @@ -764,11 +767,20 @@ actor ServerNetworkManager { connections.removeAll() connectionTasks.removeAll() pendingConnections.removeAll() + removedConnections.removeAll() await discoveryManager?.stop() } func removeConnection(_ id: UUID) async { + // Guard against redundant removal — calling stop() on an already-stopped + // connection can trigger a double-resume in the SDK's transport continuation. + guard !removedConnections.contains(id) else { + log.debug("Connection \(id) already removed, skipping") + return + } + removedConnections.insert(id) + log.debug("Removing connection: \(id)") if let connectionManager = connections[id] {