Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

StdioTransport.send() still interleaves concurrent sends under EAGAIN backpressure — #252 was closed without a merged fix

Aperta
#263 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
58/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Tranquilla
Stack tecnologico
swift
Ambito
networking

Direzione di ricerca

Start at StdioTransport.send() and inspect the write loop around the EAGAIN backpressure sleep and actor suspension. Reproduce with a slowly drained pipe, a large send, and a concurrent small send; done means concurrent newline-delimited JSON frames remain intact while backpressure is retried.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug

Summary

StdioTransport.send() can splice the bytes of one JSON-RPC frame into the
middle of another when two sends run concurrently and the first hits stdout
backpressure. This corrupts the newline-delimited framing for any client
reading the stream.

This is the same defect reported in #252 (2026-07-02). That issue was closed
by its reporter the next day with no linked commit or PR, and the code on
main today is unchanged — the bug is still present. Filing fresh with an
independent, in-the-wild reproduction.

Root cause

StdioTransport is an actor, but send(_:) contains an await inside its
write loop — the EAGAIN backpressure sleep:

} catch let error where MCPError.isResourceTemporarilyUnavailable(error) {
    try await Task.sleep(for: .milliseconds(10))   // ← actor reentrancy point
    continue
}

Actor isolation guarantees mutual exclusion only between suspension
points. While one send is suspended in that sleep mid-frame, a second
send enters the actor and writes its complete frame into the middle of the
first one.

Observed in the wild

Version 0.12.1, macOS 15 (Darwin 25.5), a stdio MCP server whose
initialize result carries a ~1.9 KB instructions string. A pipelining
client sent initialize + a tools/call back-to-back; the captured stdout
shows the tool response spliced mid-word into the initialize result:

...has not made any bucket visi{"id":2,"jsonrpc":"2.0","result":{...}}
ble to agents yet...

Both frames are unparseable to a line-delimited JSON reader at that point.
Any client that issues parallel tool calls (several do) can trigger this
whenever a response exceeds what the pipe accepts in one write — no
misbehaving client required.

Reproduction sketch

Same shape as #252's: create the transport over a pipe the reader drains
slowly, start a send large enough to hit EAGAIN (hundreds of KB, or a small
pipe), and issue a second small send while the first is suspended. The small
frame lands inside the large one.

Suggested fix

Serialize sends so one completes before the next begins — either drop to a
blocking write for the remainder of a frame, or chain sends FIFO. We shipped
the FIFO as a wrapper in our server and it eliminates the corruption:

private var lastSend: Task<Void, Never>?

func send(_ data: Data) async throws {
    let previous = lastSend
    let task = Task<Void, Error> { [base] in
        await previous?.value
        try await base.send(data)
    }
    lastSend = Task { try? await task.value }
    try await task.value
}

The same pattern inlined into StdioTransport.send (chain before the write
loop) would fix it at the source. Happy to turn this into a PR if that's
welcome.

Lingua principale
Swift
Stelle
1.5k
Fork
243
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di modelcontextprotocol/swift-sdk

Tutte le issue di modelcontextprotocol/swift-sdk

Issue simili

Altre issue su Swift

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.