Two calls in src/transports/http.jl invoke close/flush unqualified, so inside the module they resolve to the package's own Transport-only methods rather than Base, and throw MethodError at runtime. Both are swallowed by an enclosing catch, so they are invisible in normal operation.
The two sites
src/transports/http.jl:242 — in the SSE loop:
timer = Timer(0.1) # 100ms timeout
while !istaskdone(t) && isopen(timer)
...
end
close(timer) # <-- Timer, not a Transport
MethodError(ModelContextProtocol.close, (Timer(closed, timeout: 0.1 s),), ...)
src/transports/http.jl:546 — writing a response to an SSE stream:
write(sse_stream, event)
flush(sse_stream) # <-- HTTP.Streams.Stream, not a Transport
MethodError(ModelContextProtocol.flush, (HTTP.Streams.Stream{...},), ...)
The shadowing methods are flush(transport::Transport) (src/transports/base.jl:201), close(transport::HttpTransport) (http.jl:679), and the stdio equivalents.
Why it matters
The close(timer) throw happens inside the SSE stream loop, so it propagates to the outer handler, which logs "SSE stream closed" and runs a finally that deletes the stream from transport.sse_streams. That suggests server→client notification delivery over HTTP SSE is broken — progress notifications, notifications/tasks/status, and logging notifications all ride that path, and all are listed as implemented with "transport-correct delivery (stdio / SSE)".
Caveat on scope of evidence: I have verified that both MethodErrors occur and traced the control flow, but I have not proven end-to-end that no SSE notification is ever delivered. That should be confirmed as part of the fix.
How it surfaced
Observed while dry-running the rebuilt Inspector workflow (#70) against examples/simple_http_server.jl — the errors appear as level: info MCP log notifications on stderr:
"message":"Failed to write to SSE stream","error":"MethodError(ModelContextProtocol.flush, (HTTP.Streams.Stream{...
"message":"SSE stream closed","error":"MethodError(ModelContextProtocol.close, (Timer (closed, timeout: 0.1 s)...
They reproduce on main (a2508c9) and are unrelated to the Inspector change.
Fix
Qualify both as Base.close(timer) / Base.flush(sse_stream). Note http.jl:264 in the same file already does this correctly (Base.flush(stream)), and CLAUDE.md documents the footgun:
Use Base.flush() explicitly for HTTP streams to avoid naming conflicts
Worth adding a regression test that asserts an SSE notification actually arrives at a connected client, since the suite is currently green (935/935) with these paths throwing — and worth grepping for any other unqualified Base calls shadowed by the package's exports.
🤖 Generated with Claude Code
Two calls in
src/transports/http.jlinvokeclose/flushunqualified, so inside the module they resolve to the package's ownTransport-only methods rather thanBase, and throwMethodErrorat runtime. Both are swallowed by an enclosingcatch, so they are invisible in normal operation.The two sites
src/transports/http.jl:242— in the SSE loop:MethodError(ModelContextProtocol.close, (Timer(closed, timeout: 0.1 s),), ...)src/transports/http.jl:546— writing a response to an SSE stream:MethodError(ModelContextProtocol.flush, (HTTP.Streams.Stream{...},), ...)The shadowing methods are
flush(transport::Transport)(src/transports/base.jl:201),close(transport::HttpTransport)(http.jl:679), and the stdio equivalents.Why it matters
The
close(timer)throw happens inside the SSE stream loop, so it propagates to the outer handler, which logs"SSE stream closed"and runs afinallythat deletes the stream fromtransport.sse_streams. That suggests server→client notification delivery over HTTP SSE is broken — progress notifications,notifications/tasks/status, and logging notifications all ride that path, and all are listed as implemented with "transport-correct delivery (stdio / SSE)".Caveat on scope of evidence: I have verified that both
MethodErrors occur and traced the control flow, but I have not proven end-to-end that no SSE notification is ever delivered. That should be confirmed as part of the fix.How it surfaced
Observed while dry-running the rebuilt Inspector workflow (#70) against
examples/simple_http_server.jl— the errors appear aslevel: infoMCP log notifications on stderr:They reproduce on
main(a2508c9) and are unrelated to the Inspector change.Fix
Qualify both as
Base.close(timer)/Base.flush(sse_stream). Notehttp.jl:264in the same file already does this correctly (Base.flush(stream)), andCLAUDE.mddocuments the footgun:Worth adding a regression test that asserts an SSE notification actually arrives at a connected client, since the suite is currently green (935/935) with these paths throwing — and worth grepping for any other unqualified
Basecalls shadowed by the package's exports.🤖 Generated with Claude Code