`Client.connect` busy-loops at 100% CPU when the transport reaches EOF before `initialize` completes
I maintainer di solito rispondono entro 7 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 72/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- swift
- Ambito
- backend, networking
Direzione di ricerca
Start in Sources/MCP/Client/Client.swift at the message-handling task created by Client.connect and inspect how connection.receive() behaves when its AsyncThrowingStream finishes. Run the described /usr/bin/true StdioTransport reproduction first. Done means EOF stops further receive polling and connect/_initialize throws a connection-closed error instead of hanging.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
If a spawned MCP server dies (or closes its stdout) during startup — before the client's initialize handshake finishes — Client.connect(transport:) never returns and one core sits pinned at 100%.
What I see: a stdio server that exits immediately on launch. ps shows the host process in state R, and sample <pid> shows the spin inside closure #1 in Client.connect → AsyncThrowingStream.Iterator.next().
The message-handling loop in Sources/MCP/Client/Client.swift is (trimmed):
task = Task {
guard let connection = self.connection else { return }
repeat {
if Task.isCancelled { break }
do {
let stream = await connection.receive()
for try await data in stream {
if Task.isCancelled { break }
// decode + dispatch
}
} catch { break }
} while true
}
// ...
return try await _initialize()
When the transport hits EOF, the stream from receive() finishes, the for try await completes without throwing, and while true immediately loops back and calls receive() again, which returns another already-finished stream. Nothing sleeps or exits on end-of-stream, so it spins. Meanwhile connect has moved on to await _initialize(), whose response never arrives because the server is gone, so connect is parked forever on a continuation nothing resumes.
Minimal repro: spawn /usr/bin/true via Process with two Pipes, wrap the pipe fds in a StdioTransport(input:output:), build a Client, and try await client.connect(transport:). Just re-ran this against v0.12.1: connect has neither returned nor thrown 8 seconds in, and ps samples the process at ~100% CPU for the whole wait (96.6 / 100.0 / 100.0 / 100.0 over four 1.5s samples).
Two changes would fix it:
- The loop should stop (or back off) when the underlying stream finishes rather than re-polling a finished stream — a finished transport stream means the connection is gone.
connect/_initializeshould then fail with a connection-closed error instead of hanging.
For what it's worth, my workaround is to race connect against a timeout and call client.disconnect() on expiry — disconnect() cancels that task and resumes the pending initialize with an error, so connect throws instead of hanging. Separately I had to set signal(SIGPIPE, SIG_IGN), because writing to the dead server's stdin was raising SIGPIPE and taking down the whole host process. I'd be happy to turn the loop-termination part into a PR if that's welcome.
- Lingua principale
- Swift
- Stelle
- 1.5k
- Fork
- 252
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di modelcontextprotocol/swift-sdk
-
NetworkTransport leaks the underlying socket (CLOSE_WAIT) when reconnection is disabled and the receive loop terminatesForse già presa @skirrellyjones l’ha presa 37 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
modelcontextprotocol/swift-sdk#282 ·
I maintainer di solito rispondono entro 7 giorni
-
Value decoding changes data-URL-looking JSON strings into Value.dataForse già presa @bitbemol l’ha presa 48 giorni fa. Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
modelcontextprotocol/swift-sdk#277 ·
I maintainer di solito rispondono entro 7 giorni
-
enhancement
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 78/100
modelcontextprotocol/swift-sdk#274 ·
I maintainer di solito rispondono entro 7 giorni
-
`HTTPClientTransport` fails to build on Windows: `#if !os(Linux)` guards no longer match the `EventSource` platform conditionForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
modelcontextprotocol/swift-sdk#261 ·
I maintainer di solito rispondono entro 7 giorni
-
Value: add numberValue (Double?) that coerces .int and .doubleForse già presa @gsdali l’ha presa 159 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
modelcontextprotocol/swift-sdk#225 ·
I maintainer di solito rispondono entro 7 giorni
Tutte le issue di modelcontextprotocol/swift-sdk
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
[BUG] File transcription skips audio chunks shorter than one secondForse già presa @sabbah13 l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
altic-dev/FluidVoice#1161 ·
I maintainer di solito rispondono entro 1 giorno
-
swift.md: `Date(myString, strategy: .iso8601)` example omits `try` — the initializer is throwingAperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
-
core: RAC_LLM_OPTIONS_DEFAULT.min_p disagrees with the IDL's annotated defaultForse già presa Una pull request collegata a questa issue è aperta o già unita. Apertabug core p3
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
RunanywhereAI/runanywhere-sdks#1083 ·
I maintainer di solito rispondono entro 5 giorni
-
bug/fix needs: triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
capawesome-team/capacitor-firebase#1054 ·
I maintainer di solito rispondono entro 1 giorno