1111from pathlib import Path
1212from typing import Literal , ParamSpec , TypeVar , cast
1313
14- __all__ = ' run_mcp_server' , ' DenoEnv' , ' prepare_deno_env' , ' async_prepare_deno_env'
14+ __all__ = " run_mcp_server" , " DenoEnv" , " prepare_deno_env" , " async_prepare_deno_env"
1515
1616logger = logging .getLogger (__name__ )
1717LoggingLevel = Literal ['debug' , 'info' , 'notice' , 'warning' , 'error' , 'critical' , 'alert' , 'emergency' ]
@@ -23,8 +23,9 @@ def run_mcp_server(
2323 mode : Mode ,
2424 * ,
2525 http_port : int | None = None ,
26+ http_host : str | None = None ,
2627 dependencies : list [str ] | None = None ,
27- return_mode : Literal [' json' , ' xml' ] = ' xml' ,
28+ return_mode : Literal [" json" , " xml" ] = " xml" ,
2829 deps_log_handler : LogHandler | None = None ,
2930 allow_networking : bool = True ,
3031 verbose : bool = False ,
@@ -34,6 +35,7 @@ def run_mcp_server(
3435 Args:
3536 mode: The mode to run the server in.
3637 http_port: The port to run the server on if mode is `streamable_http`.
38+ http_host: The host to run the server on if mode is `streamable_http`.
3739 dependencies: The dependencies to install.
3840 return_mode: The mode to return tool results in.
3941 deps_log_handler: Optional function to receive logs emitted while installing dependencies.
@@ -49,19 +51,22 @@ def run_mcp_server(
4951 mode ,
5052 dependencies = dependencies ,
5153 http_port = http_port ,
54+ http_host = http_host ,
5255 return_mode = return_mode ,
5356 deps_log_handler = deps_log_handler ,
5457 allow_networking = allow_networking ,
5558 ) as env :
5659 if mode in ('streamable_http' , 'streamable_http_stateless' ):
5760 logger .info ('Running mcp-run-python via %s on port %d...' , mode , http_port )
5861 else :
59- logger .info (' Running mcp-run-python via %s...' , mode )
62+ logger .info (" Running mcp-run-python via %s..." , mode )
6063
6164 try :
62- p = subprocess .run (('deno' , * env .args ), cwd = env .cwd , stdout = stdout , stderr = stderr )
65+ p = subprocess .run (
66+ ("deno" , * env .args ), cwd = env .cwd , stdout = stdout , stderr = stderr
67+ )
6368 except KeyboardInterrupt : # pragma: no cover
64- logger .warning (' Server stopped.' )
69+ logger .warning (" Server stopped." )
6570 return 0
6671 else :
6772 return p .returncode
@@ -78,8 +83,9 @@ def prepare_deno_env(
7883 mode : Mode ,
7984 * ,
8085 http_port : int | None = None ,
86+ http_host : str | None = None ,
8187 dependencies : list [str ] | None = None ,
82- return_mode : Literal [' json' , ' xml' ] = ' xml' ,
88+ return_mode : Literal [" json" , " xml" ] = " xml" ,
8389 deps_log_handler : LogHandler | None = None ,
8490 allow_networking : bool = True ,
8591) -> Iterator [DenoEnv ]:
@@ -92,6 +98,7 @@ def prepare_deno_env(
9298 Args:
9399 mode: The mode to run the server in.
94100 http_port: The port to run the server on if mode is `streamable_http`.
101+ http_host: The host to run the server on if mode is `streamable_http`.
95102 dependencies: The dependencies to install.
96103 return_mode: The mode to return tool results in.
97104 deps_log_handler: Optional function to receive logs emitted while installing dependencies.
@@ -101,22 +108,22 @@ def prepare_deno_env(
101108 Returns:
102109 Yields the deno environment details.
103110 """
104- cwd = Path (tempfile .mkdtemp ()) / ' mcp-run-python'
111+ cwd = Path (tempfile .mkdtemp ()) / " mcp-run-python"
105112 try :
106- src = Path (__file__ ).parent / ' deno'
107- logger .debug (' Copying from %s to %s...' , src , cwd )
113+ src = Path (__file__ ).parent / " deno"
114+ logger .debug (" Copying from %s to %s..." , src , cwd )
108115 shutil .copytree (src , cwd )
109- logger .info (' Installing dependencies %s...' , dependencies )
116+ logger .info (" Installing dependencies %s..." , dependencies )
110117
111- args = ' deno' , * _deno_install_args (dependencies )
118+ args = " deno" , * _deno_install_args (dependencies )
112119 p = subprocess .Popen (args , cwd = cwd , stdout = subprocess .PIPE , stderr = subprocess .STDOUT , text = True )
113120 stdout : list [str ] = []
114121 if p .stdout is not None :
115122 for line in p .stdout :
116123 line = line .strip ()
117124 if deps_log_handler :
118- parts = line .split ('|' , 1 )
119- level , msg = parts if len (parts ) == 2 else (' info' , line )
125+ parts = line .split ("|" , 1 )
126+ level , msg = parts if len (parts ) == 2 else (" info" , line )
120127 deps_log_handler (cast (LoggingLevel , level ), msg )
121128 stdout .append (line )
122129 p .wait ()
@@ -126,6 +133,7 @@ def prepare_deno_env(
126133 args = _deno_run_args (
127134 mode ,
128135 http_port = http_port ,
136+ http_host = http_host ,
129137 dependencies = dependencies ,
130138 return_mode = return_mode ,
131139 allow_networking = allow_networking ,
@@ -141,8 +149,9 @@ async def async_prepare_deno_env(
141149 mode : Mode ,
142150 * ,
143151 http_port : int | None = None ,
152+ http_host : str | None = None ,
144153 dependencies : list [str ] | None = None ,
145- return_mode : Literal [' json' , ' xml' ] = ' xml' ,
154+ return_mode : Literal [" json" , " xml" ] = " xml" ,
146155 deps_log_handler : LogHandler | None = None ,
147156 allow_networking : bool = True ,
148157) -> AsyncIterator [DenoEnv ]:
@@ -151,6 +160,7 @@ async def async_prepare_deno_env(
151160 prepare_deno_env ,
152161 mode ,
153162 http_port = http_port ,
163+ http_host = http_host ,
154164 dependencies = dependencies ,
155165 return_mode = return_mode ,
156166 deps_log_handler = deps_log_handler ,
@@ -164,13 +174,13 @@ async def async_prepare_deno_env(
164174
165175def _deno_install_args (dependencies : list [str ] | None = None ) -> list [str ]:
166176 args = [
167- ' run' ,
168- ' --allow-net' ,
169- ' --allow-read=./node_modules' ,
170- ' --allow-write=./node_modules' ,
171- ' --node-modules-dir=auto' ,
172- ' src/main.ts' ,
173- ' noop' ,
177+ " run" ,
178+ " --allow-net" ,
179+ " --allow-read=./node_modules" ,
180+ " --allow-write=./node_modules" ,
181+ " --node-modules-dir=auto" ,
182+ " src/main.ts" ,
183+ " noop" ,
174184 ]
175185 if dependencies is not None :
176186 args .append (f'--deps={ "," .join (dependencies )} ' )
@@ -181,32 +191,35 @@ def _deno_run_args(
181191 mode : Mode ,
182192 * ,
183193 http_port : int | None = None ,
194+ http_host : str | None = None ,
184195 dependencies : list [str ] | None = None ,
185- return_mode : Literal [' json' , ' xml' ] = ' xml' ,
196+ return_mode : Literal [" json" , " xml" ] = " xml" ,
186197 allow_networking : bool = True ,
187198) -> list [str ]:
188- args = [' run' ]
199+ args = [" run" ]
189200 if allow_networking :
190- args += [' --allow-net' ]
201+ args += [" --allow-net" ]
191202 args += [
192- ' --allow-read=./node_modules' ,
193- ' --node-modules-dir=auto' ,
194- ' src/main.ts' ,
203+ " --allow-read=./node_modules" ,
204+ " --node-modules-dir=auto" ,
205+ " src/main.ts" ,
195206 mode ,
196- f' --return-mode={ return_mode } ' ,
207+ f" --return-mode={ return_mode } " ,
197208 ]
198209 if dependencies is not None :
199210 args .append (f'--deps={ "," .join (dependencies )} ' )
200- if http_port is not None :
201- if mode in ('streamable_http' , 'streamable_http_stateless' ):
202- args .append (f'--port={ http_port } ' )
203- else :
204- raise ValueError ('Port is only supported for `streamable_http` modes' )
211+ if mode in ('streamable_http' , 'streamable_http_stateless' ):
212+ if http_port is not None :
213+ args .append (f"--port={ http_port } " )
214+ if http_host is not None :
215+ args .append (f"--host={ http_host } " )
216+ elif http_port is not None or http_host is not None :
217+ raise ValueError ("Port and host are only supported for `streamable_http` mode" )
205218 return args
206219
207220
208- P = ParamSpec ('P' )
209- T = TypeVar ('T' )
221+ P = ParamSpec ("P" )
222+ T = TypeVar ("T" )
210223
211224
212225async def _asyncify (func : Callable [P , T ], * args : P .args , ** kwargs : P .kwargs ) -> T :
0 commit comments