@@ -34,13 +34,13 @@ defmodule McpProxy.SSE do
3434 end
3535
3636 @ impl true
37- def init ( { sse_url , opts } ) do
37+ def init ( sse_url ) do
3838 { :ok ,
3939 % {
4040 url: sse_url ,
4141 endpoint: nil ,
42- debug: Keyword . get ( opts , :debug , false ) ,
43- max_disconnected_time: Keyword . get ( opts , :max_disconnected_time ) ,
42+ debug: Application . get_env ( :mcp_proxy , :debug , false ) ,
43+ max_disconnected_time: Application . get_env ( :mcp_proxy , :max_disconnected_time ) ,
4444 disconnected_since: nil ,
4545 state: :connecting ,
4646 connect_tries: 0 ,
@@ -263,11 +263,17 @@ defmodule McpProxy.SSE do
263263 end
264264
265265 # regular events
266- def handle_info ( { :sse_event , { :message , event } } , state ) ,
267- do: handle_event ( event , state , & IO . puts ( Jason . encode! ( & 1 ) ) )
266+ def handle_info ( { :sse_event , { :message , event } } , state ) do
267+ handle_event ( event , state , { & IO . puts ( Jason . encode! ( & 1 ) ) , fn _ -> :ok end } )
268+ end
268269
269- def handle_info ( { :io_event , event } , state ) ,
270- do: handle_event ( event , state , & forward_message ( & 1 , state . endpoint , state . debug ) )
270+ def handle_info ( { :io_event , event } , state ) do
271+ handle_event (
272+ event ,
273+ state ,
274+ { & forward_message ( & 1 , state . endpoint , state . debug ) , & IO . puts ( Jason . encode! ( & 1 ) ) }
275+ )
276+ end
271277
272278 # whenever the HTTP process dies, we try to reconnect
273279 def handle_info ( { :DOWN , _ref , :process , http_pid , _reason } , % { http_pid: http_pid } = state ) do
@@ -327,52 +333,84 @@ defmodule McpProxy.SSE do
327333 Enum . reduce ( messages , state , fn message , state -> handle_event ( message , state , handler ) end )
328334 end
329335
330- defp handle_event ( % { "jsonrpc" => "2.0" , "id" => request_id } = event , state , handler )
336+ defp handle_event ( % { "jsonrpc" => "2.0" , "id" => request_id } = event , state , { req , resp } )
331337 when is_request ( event ) do
332338 # whenever we get a request from the client (OR the server!)
333339 # we generate a random ID to prevent duplicate IDs, for example when
334340 # a reconnected server decides to send a ping and always starts with ID 0
335341 new_id = random_id! ( )
336342 event = Map . put ( event , "id" , new_id )
337343
338- handler . ( event )
344+ state =
345+ case req . ( event ) do
346+ :ok ->
347+ % { state | id_map: Map . put ( state . id_map , new_id , request_id ) }
348+
349+ { :reply_error , reply } ->
350+ resp . ( Map . put ( reply , "id" , request_id ) )
351+ # we don't store the new_id when we already replied to prevent duplicates;
352+ # instead, we'll log an error if a server reply is received later and we already
353+ # replied
354+ state
355+ end
339356
340- { :noreply , % { state | id_map: Map . put ( state . id_map , new_id , request_id ) } }
357+ { :noreply , state }
341358 end
342359
343- defp handle_event ( % { "jsonrpc" => "2.0" , "id" => response_id } = event , state , handler )
360+ defp handle_event ( % { "jsonrpc" => "2.0" , "id" => response_id } = event , state , { handler , _ } )
344361 when is_response ( event ) do
345362 # whenever we receive a response (from the client or server)
346363 # we fetch the original ID from the id map to present the expected
347364 # ID in the reply
348- original_id = Map . fetch! ( state . id_map , response_id )
349- event = Map . put ( event , "id" , original_id )
365+ case state . id_map do
366+ % { ^ response_id => original_id } ->
367+ event = Map . put ( event , "id" , original_id )
368+
369+ handler . ( event )
370+
371+ { :noreply , % { state | id_map: Map . delete ( state . id_map , response_id ) } }
350372
351- handler . ( event )
373+ _ ->
374+ Logger . error (
375+ "Did not find original ID for response: #{ response_id } . Discarding response!"
376+ )
352377
353- { :noreply , % { state | id_map: Map . delete ( state . id_map , response_id ) } }
378+ { :noreply , state }
379+ end
354380 end
355381
356382 # no id, so must be a notification that we can just forward as is
357- defp handle_event ( % { "jsonrpc" => "2.0" } = event , state , handler ) do
358- handler . ( event )
383+ defp handle_event ( % { "jsonrpc" => "2.0" } = event , state , { req , _ } ) do
384+ req . ( event )
359385
360386 { :noreply , state }
361387 end
362388
363389 ## other helpers
364390
365391 defp forward_message ( message , endpoint , debug ) do
366- try do
367- if debug , do: Logger . debug ( "Forwarding request to server: #{ inspect ( message ) } " )
368- Req . post! ( endpoint , json: message )
369- rescue
370- error ->
371- Logger . error (
372- "Failed to forward message: #{ Exception . format ( :error , error , __STACKTRACE__ ) } "
373- )
374-
375- # TODO: store message and replay later?
392+ if debug , do: Logger . debug ( "Forwarding request to server: #{ inspect ( message ) } " )
393+
394+ case Req . post ( endpoint ,
395+ json: message ,
396+ receive_timeout: Application . get_env ( :mcp_proxy , :receive_timeout , 60_000 )
397+ ) do
398+ { :ok , _ } ->
399+ # even when the server replies with a status code that is not in the 200 range
400+ # it might still send a reply on the SSE connection
401+ :ok
402+
403+ { :error , % Req.TransportError { reason: reason } } ->
404+ Logger . error ( "Failed to forward message #{ inspect ( message ) } :\n #{ inspect ( reason ) } " )
405+
406+ { :reply_error ,
407+ % {
408+ jsonrpc: "2.0" ,
409+ error: % {
410+ code: - 32011 ,
411+ message: "Failed to forward request. Reason: #{ inspect ( reason ) } "
412+ }
413+ } }
376414 end
377415 end
378416
0 commit comments