@@ -36,6 +36,80 @@ def make_non_blocking(file_obj):
3636 fcntl .fcntl (fd , fcntl .F_SETFL , fl | os .O_NONBLOCK )
3737
3838
39+ def wait_for_random_port (process : subprocess .Popen , timeout : int = 20 ) -> Optional [int ]:
40+ """
41+ Wait for mcp-proxy to output the random port information.
42+
43+ Args:
44+ process: The mcp-proxy process
45+ timeout: Maximum time to wait in seconds
46+
47+ Returns:
48+ The detected port number or None if not found
49+ """
50+ console .print ("[cyan]Waiting for mcp-proxy to start...[/]" )
51+
52+ # Wait for mcp-proxy to output the port information
53+ start_time = time .time ()
54+ port_found = False
55+ actual_port = None
56+
57+ # Port detection loop
58+ while time .time () - start_time < timeout and not port_found :
59+ # Check if process is still running
60+ if process .poll () is not None :
61+ # Process terminated prematurely
62+ stderr_output = ""
63+ try :
64+ if process .stderr :
65+ stderr_output = process .stderr .read () or ""
66+ except (IOError , OSError ):
67+ pass
68+
69+ console .print ("[bold red]Error:[/] mcp-proxy terminated unexpectedly" )
70+ console .print (f"[red]Error output:[/]\n { stderr_output } " )
71+ sys .exit (1 )
72+
73+ # Use select to wait for data to be available without blocking
74+ readable = []
75+ if process .stdout :
76+ readable .append (process .stdout )
77+ if process .stderr :
78+ readable .append (process .stderr )
79+
80+ if readable :
81+ # Wait for up to 1 second for output
82+ r , _ , _ = select .select (readable , [], [], 1.0 )
83+
84+ # Process available output
85+ for stream in r :
86+ try :
87+ line = stream .readline ()
88+ if line :
89+ print (line .rstrip ())
90+
91+ # Check for port information
92+ if "Uvicorn running on http://" in line :
93+ try :
94+ url_part = line .split ("Uvicorn running on " )[1 ].split (" " )[0 ]
95+ actual_port = int (url_part .split (":" )[- 1 ].strip ())
96+ port_found = True
97+ console .print (
98+ f"[cyan]mcp-proxy SSE server running on port [bold]{ actual_port } [/bold][/]"
99+ )
100+ break
101+ except (ValueError , IndexError ):
102+ pass
103+ except (IOError , OSError ):
104+ # Resource temporarily unavailable - this is normal for non-blocking IO
105+ pass
106+ else :
107+ # No streams to read from, just wait a bit
108+ time .sleep (0.5 )
109+
110+ return actual_port
111+
112+
39113def start_mcp_proxy (command : str , port : Optional [int ] = None ) -> Tuple [subprocess .Popen , int ]:
40114 """
41115 Start mcp-proxy to convert a stdio MCP server to an SSE server.
@@ -79,87 +153,14 @@ def start_mcp_proxy(command: str, port: Optional[int] = None) -> Tuple[subproces
79153 # If port is None, we need to parse the output to find the random port
80154 actual_port = port
81155 if not actual_port :
82- console .print ("[cyan]Waiting for mcp-proxy to start...[/]" )
83-
84- # Wait for mcp-proxy to output the port information
85- start_time = time .time ()
86- port_found = False
87- timeout = 20 # Allow more time (20 seconds) for the server to start
156+ actual_port = wait_for_random_port (process )
88157
89- # Port detection loop
90- while time .time () - start_time < timeout and not port_found :
91- # Check if process is still running
92- if process .poll () is not None :
93- # Process terminated prematurely
94- stderr_output = ""
95- try :
96- if process .stderr :
97- stderr_output = process .stderr .read () or ""
98- except (IOError , OSError ):
99- pass
100-
101- console .print ("[bold red]Error:[/] mcp-proxy terminated unexpectedly" )
102- console .print (f"[red]Error output:[/]\n { stderr_output } " )
103- sys .exit (1 )
104-
105- # Use select to wait for data to be available without blocking
106- readable = []
107- if process .stdout :
108- readable .append (process .stdout )
109- if process .stderr :
110- readable .append (process .stderr )
111-
112- if readable :
113- # Wait for up to 1 second for output
114- r , _ , _ = select .select (readable , [], [], 1.0 )
115-
116- # Process available output
117- for stream in r :
118- try :
119- line = stream .readline ()
120- if line :
121- print (line .rstrip ())
122-
123- # Check for port information
124- if "Serving on" in line :
125- try :
126- actual_port = int (line .split (":" )[- 1 ].strip ())
127- port_found = True
128- console .print (
129- f"[cyan]mcp-proxy SSE server running on port [bold]{ actual_port } [/bold][/]"
130- )
131- break
132- except (ValueError , IndexError ):
133- pass
134- elif "Uvicorn running on http://" in line :
135- try :
136- url_part = line .split ("Uvicorn running on " )[1 ].split (" " )[0 ]
137- actual_port = int (url_part .split (":" )[- 1 ].strip ())
138- port_found = True
139- console .print (
140- f"[cyan]mcp-proxy SSE server running on port [bold]{ actual_port } [/bold][/]"
141- )
142- break
143- except (ValueError , IndexError ):
144- pass
145- except (IOError , OSError ):
146- # Resource temporarily unavailable - this is normal for non-blocking IO
147- pass
148- else :
149- # No streams to read from, just wait a bit
150- time .sleep (0.5 )
151-
152- # After the loop, check if we found the port
153- if not port_found and not actual_port :
158+ # Check if we found the port
159+ if not actual_port :
154160 console .print ("[bold red]Error:[/] Could not determine the port mcp-proxy is running on" )
155161 process .terminate ()
156162 sys .exit (1 )
157163
158- if not actual_port :
159- console .print ("[bold red]Error:[/] Could not determine the port mcp-proxy is running on" )
160- process .terminate ()
161- sys .exit (1 )
162-
163164 return process , actual_port
164165
165166
0 commit comments