2
2
Share command for MCPM - Share a single MCP server through a tunnel
3
3
"""
4
4
5
- import os
6
5
import secrets
7
- import select
8
6
import shlex
9
7
import shutil
10
8
import signal
@@ -27,19 +25,6 @@ def find_mcp_proxy() -> Optional[str]:
27
25
return shutil .which ("mcp-proxy" )
28
26
29
27
30
- def make_non_blocking (file_obj ):
31
- """Make a file object non-blocking."""
32
- if os .name == 'posix' :
33
- import fcntl
34
-
35
- fd = file_obj .fileno ()
36
- fl = fcntl .fcntl (fd , fcntl .F_GETFL )
37
- fcntl .fcntl (fd , fcntl .F_SETFL , fl | os .O_NONBLOCK )
38
- # On other platforms (e.g., Windows), we rely on the behavior of select()
39
- # and the non-blocking nature of readline() on Popen streams,
40
- # or the existing try-except for IOError/OSError.
41
-
42
-
43
28
def wait_for_random_port (process : subprocess .Popen , timeout : int = 20 ) -> Optional [int ]:
44
29
"""
45
30
Wait for mcp-proxy to output the random port information.
@@ -74,39 +59,26 @@ def wait_for_random_port(process: subprocess.Popen, timeout: int = 20) -> Option
74
59
console .print (f"[red]Error output:[/]\n { stderr_output } " )
75
60
sys .exit (1 )
76
61
77
- # Use select to wait for data to be available without blocking
78
- readable = []
79
- if process .stdout :
80
- readable .append (process .stdout )
81
- if process .stderr :
82
- readable .append (process .stderr )
83
-
84
- if readable :
85
- # Wait for up to 1 second for output
86
- r , _ , _ = select .select (readable , [], [], 1.0 )
87
-
88
- # Process available output
89
- for stream in r :
90
- try :
91
- line = stream .readline ()
92
- if line :
93
- print (line .rstrip ())
94
-
95
- # Check for port information
96
- if "Uvicorn running on http://" in line :
97
- try :
98
- url_part = line .split ("Uvicorn running on " )[1 ].split (" " )[0 ]
99
- actual_port = int (url_part .split (":" )[- 1 ].strip ())
100
- port_found = True
101
- console .print (
102
- f"[cyan]mcp-proxy SSE server running on port [bold]{ actual_port } [/bold][/]"
103
- )
104
- break
105
- except (ValueError , IndexError ):
106
- pass
107
- except (IOError , OSError ):
108
- # Resource temporarily unavailable - this is normal for non-blocking IO
109
- pass
62
+ # Process available output
63
+ try :
64
+ if process .stderr :
65
+ line = process .stderr .readline ()
66
+ if line :
67
+ console .print (line .rstrip ())
68
+
69
+ # Check for port information
70
+ if "Uvicorn running on http://" in line :
71
+ try :
72
+ url_part = line .split ("Uvicorn running on " )[1 ].split (" " )[0 ]
73
+ actual_port = int (url_part .split (":" )[- 1 ].strip ())
74
+ port_found = True
75
+ console .print (f"[cyan]mcp-proxy SSE server running on port [bold]{ actual_port } [/bold][/]" )
76
+ break
77
+ except (ValueError , IndexError ):
78
+ pass
79
+ except (IOError , OSError ):
80
+ # Resource temporarily unavailable - this is normal for non-blocking IO
81
+ pass
110
82
else :
111
83
# No streams to read from, just wait a bit
112
84
time .sleep (0.5 )
@@ -148,12 +120,6 @@ def start_mcp_proxy(command: str, port: Optional[int] = None) -> Tuple[subproces
148
120
console .print (f"[cyan]Running command: [bold]{ ' ' .join (cmd_parts )} [/bold][/]" )
149
121
process = subprocess .Popen (cmd_parts , stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True , bufsize = 1 )
150
122
151
- # Make stdout and stderr non-blocking
152
- if process .stdout :
153
- make_non_blocking (process .stdout )
154
- if process .stderr :
155
- make_non_blocking (process .stderr )
156
-
157
123
# If port is None, we need to parse the output to find the random port
158
124
actual_port = port
159
125
if not actual_port :
@@ -352,45 +318,31 @@ def signal_handler(sig, frame):
352
318
if tunnel :
353
319
tunnel .kill ()
354
320
break
355
-
356
- # Use select to check for available output without blocking
357
- readable = []
358
- if server_process .stdout :
359
- readable .append (server_process .stdout )
360
- if server_process .stderr :
361
- readable .append (server_process .stderr )
362
-
363
- if readable :
364
- # Wait for up to 1 second for output
365
- r , _ , _ = select .select (readable , [], [], 1.0 )
366
-
367
- # Process available output
368
- for stream in r :
369
- try :
370
- line = stream .readline ()
371
- if line :
372
- line_str = line .rstrip ()
373
- print (line_str )
374
- last_activity_time = time .time ()
375
-
376
- # Check for error messages
377
- error_msg = monitor_for_errors (line_str )
378
- if error_msg and error_msg not in error_messages :
379
- console .print (f"[bold red]Error:[/] { error_msg } " )
380
- error_messages .append (error_msg )
381
- server_error_detected = True
382
-
383
- # If this is a critical error and we have retries left, restart
384
- if "Protocol initialization error" in error_msg and retries_left > 0 :
385
- console .print (
386
- f"[yellow]Will attempt to restart ({ retries_left } retries left)[/]"
387
- )
388
- # Break out of the loop to trigger a restart
389
- server_process .terminate ()
390
- break
391
- except (IOError , OSError ):
392
- # Resource temporarily unavailable - this is normal for non-blocking IO
393
- pass
321
+ # Process available output
322
+ try :
323
+ if server_process .stderr :
324
+ line = server_process .stderr .readline ()
325
+ if line :
326
+ line_str = line .rstrip ()
327
+ console .print (line_str )
328
+ last_activity_time = time .time ()
329
+
330
+ # Check for error messages
331
+ error_msg = monitor_for_errors (line_str )
332
+ if error_msg and error_msg not in error_messages :
333
+ console .print (f"[bold red]Error:[/] { error_msg } " )
334
+ error_messages .append (error_msg )
335
+ server_error_detected = True
336
+
337
+ # If this is a critical error and we have retries left, restart
338
+ if "Protocol initialization error" in error_msg and retries_left > 0 :
339
+ console .print (f"[yellow]Will attempt to restart ({ retries_left } retries left)[/]" )
340
+ # Break out of the loop to trigger a restart
341
+ server_process .terminate ()
342
+ break
343
+ except (IOError , OSError ):
344
+ # Resource temporarily unavailable - this is normal for non-blocking IO
345
+ pass
394
346
else :
395
347
# No streams to read from, just wait a bit
396
348
time .sleep (0.5 )
0 commit comments