File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ ## Unreleased
2+
3+ - Fix ` Plug.Conn.AlreadySentError ` when a second SSE GET arrives for an
4+ existing session. The conflict response (` 409 -32000 ` ) is now returned
5+ cleanly without attempting to write streaming headers on the sent conn.
6+
17## 0.4.3 (2026-04-03)
28
39- Fix invalid response when client request an invalid resource_uri
Original file line number Diff line number Diff line change @@ -278,17 +278,22 @@ defmodule Phantom.Plug do
278278
279279 defp dispatch ( % Plug.Conn { method: "GET" } = conn , opts ) do
280280 if opts . pubsub do
281- conn = maybe_track_session_stream ( conn )
282- session = conn . private . phantom . session
281+ case maybe_track_session_stream ( conn ) do
282+ % Plug.Conn { halted: true } = conn ->
283+ conn
283284
284- conn
285- |> put_resp_header ( "mcp-session-id" , session . id )
286- |> put_resp_header ( "cache-control" , "no-cache, no-transform" )
287- |> put_resp_content_type ( "text/event-stream" )
288- |> put_resp_header ( "connection" , "keep-alive" )
289- |> put_resp_header ( "x-accel-buffering" , "no" )
290- |> send_chunked ( 202 )
291- |> stream_loop ( opts )
285+ conn ->
286+ session = conn . private . phantom . session
287+
288+ conn
289+ |> put_resp_header ( "mcp-session-id" , session . id )
290+ |> put_resp_header ( "cache-control" , "no-cache, no-transform" )
291+ |> put_resp_content_type ( "text/event-stream" )
292+ |> put_resp_header ( "connection" , "keep-alive" )
293+ |> put_resp_header ( "x-accel-buffering" , "no" )
294+ |> send_chunked ( 202 )
295+ |> stream_loop ( opts )
296+ end
292297 else
293298 conn
294299 |> put_status ( 405 )
Original file line number Diff line number Diff line change @@ -299,6 +299,28 @@ defmodule Phantom.PlugTest do
299299 assert_sse_connected ( )
300300 assert Phantom.Tracker . list_sessions ( ) != [ ]
301301 end
302+
303+ test "second GET on same session returns 409 without raising AlreadySentError" do
304+ session_id = "019dd3d8-0000-0000-0000-000000000001"
305+
306+ :get
307+ |> conn ( "/mcp" )
308+ |> put_req_header ( "accept" , "text/event-stream" )
309+ |> call ( session_id: session_id )
310+
311+ assert_sse_connected ( )
312+
313+ :get
314+ |> conn ( "/mcp" )
315+ |> put_req_header ( "accept" , "text/event-stream" )
316+ |> call ( session_id: session_id )
317+
318+ assert_receive { :conn , conn }
319+ assert conn . status == 409
320+ error = JSON . decode! ( conn . resp_body )
321+ assert error [ "error" ] [ "code" ] == - 32000
322+ assert error [ "error" ] [ "message" ] == "Only one SSE stream is allowed per session"
323+ end
302324 end
303325
304326 test "handles prompt responses" do
You can’t perform that action at this time.
0 commit comments