11from __future__ import annotations
22
3+ import json
34import os
45from importlib import import_module
56from pathlib import Path
@@ -173,6 +174,51 @@ class ExitCode:
173174 RETRYABLE = 75 # EX_TEMPFAIL from sysexits.h
174175
175176
177+ def _load_mcp_configs_from_cli_inputs (
178+ mcp_config_file : list [Path ] | None ,
179+ mcp_config : list [str ] | None ,
180+ ) -> list [Any ]:
181+ """Load MCP config JSON from the current CLI inputs.
182+
183+ This intentionally re-resolves the default global MCP file on every call so
184+ `/reload` observes servers added after the process started.
185+ """
186+ from .mcp import get_global_mcp_config_file
187+
188+ file_configs = list (mcp_config_file or [])
189+ raw_mcp_config = list (mcp_config or [])
190+
191+ # Use default MCP config file if no MCP config file is provided. Keep this
192+ # lookup live for reloads: the file may be created after process startup.
193+ if not file_configs :
194+ default_mcp_file = get_global_mcp_config_file ()
195+ if default_mcp_file .exists ():
196+ file_configs .append (default_mcp_file )
197+
198+ configs : list [Any ] = []
199+ for conf in file_configs :
200+ try :
201+ configs .append (json .loads (conf .read_text (encoding = "utf-8" )))
202+ except json .JSONDecodeError as e :
203+ raise typer .BadParameter (
204+ f"Invalid JSON in MCP config file { conf } : { e } " ,
205+ param_hint = "--mcp-config-file" ,
206+ ) from e
207+ except OSError as e :
208+ raise typer .BadParameter (
209+ f"Cannot read MCP config file { conf } : { e } " ,
210+ param_hint = "--mcp-config-file" ,
211+ ) from e
212+
213+ for conf in raw_mcp_config :
214+ try :
215+ configs .append (json .loads (conf ))
216+ except json .JSONDecodeError as e :
217+ raise typer .BadParameter (f"Invalid JSON: { e } " , param_hint = "--mcp-config" ) from e
218+
219+ return configs
220+
221+
176222InputFormat = Literal ["text" , "stream-json" ]
177223OutputFormat = Literal ["text" , "stream-json" ]
178224
@@ -502,7 +548,6 @@ def pythinker(
502548 """Pythinker, your next CLI agent."""
503549 import asyncio
504550 import contextlib
505- import json
506551
507552 from pythinker_code .utils .proctitle import init_process_name
508553
@@ -525,8 +570,6 @@ def pythinker(
525570 from pythinker_code .ui .shell .startup import ShellStartupProgress
526571 from pythinker_code .utils .logging import logger , open_original_stderr , redirect_stderr_to_logger
527572
528- from .mcp import get_global_mcp_config_file
529-
530573 # Don't redirect stderr during argument parsing. Our stderr redirector
531574 # replaces fd=2 with a pipe, which would swallow Click/Typer startup errors.
532575 # Redirection is installed later, right before PythinkerCLI.create(), so that
@@ -648,25 +691,6 @@ def _emit_fatal_error(message: str) -> None:
648691 elif config_file is not None :
649692 config = config_file
650693
651- file_configs = list (mcp_config_file or [])
652- raw_mcp_config = list (mcp_config or [])
653-
654- # Use default MCP config file if no MCP config is provided
655- if not file_configs :
656- default_mcp_file = get_global_mcp_config_file ()
657- if default_mcp_file .exists ():
658- file_configs .append (default_mcp_file )
659-
660- try :
661- mcp_configs = [json .loads (conf .read_text (encoding = "utf-8" )) for conf in file_configs ]
662- except json .JSONDecodeError as e :
663- raise typer .BadParameter (f"Invalid JSON: { e } " , param_hint = "--mcp-config-file" ) from e
664-
665- try :
666- mcp_configs += [json .loads (conf ) for conf in raw_mcp_config ]
667- except json .JSONDecodeError as e :
668- raise typer .BadParameter (f"Invalid JSON: { e } " , param_hint = "--mcp-config" ) from e
669-
670694 # Honor --no-telemetry by exporting the env var before any subsystem (Sentry,
671695 # OTel, sink) reads it during PythinkerCLI.create.
672696 if no_telemetry :
@@ -748,6 +772,8 @@ async def _run(session_id: str | None, prefill_text: str | None = None) -> tuple
748772 if changed :
749773 session .save_state ()
750774
775+ mcp_configs = _load_mcp_configs_from_cli_inputs (mcp_config_file , mcp_config )
776+
751777 # Redirect stderr *before* PythinkerCLI.create() so that MCP server
752778 # subprocesses (e.g. mcp-remote OAuth debug logs) write to the log
753779 # file instead of polluting the user's terminal. CLI argument
@@ -899,9 +925,11 @@ async def _run(session_id: str | None, prefill_text: str | None = None) -> tuple
899925 timeout = 5 ,
900926 )
901927
902- if not preserve_background_tasks :
928+ if preserve_background_tasks :
929+ await instance .cleanup_runtime_resources ()
930+ else :
903931 await instance .shutdown_background_tasks ()
904- await instance .await_bg_tasks_shutdown ()
932+ await instance .await_bg_tasks_shutdown ()
905933
906934 return session , exit_code
907935 finally :
0 commit comments