From dba0dc53fe4ec465c9524a8a213d90e7cb2a5b88 Mon Sep 17 00:00:00 2001 From: Jonathan Wang Date: Thu, 24 Apr 2025 18:27:06 +0800 Subject: [PATCH 1/2] feat: router share with https by default --- .gitignore | 1 + docs/dev_guide.md | 9 +++++++++ src/mcpm/commands/router.py | 12 +++++++----- src/mcpm/router/app.py | 4 ++-- src/mcpm/router/share.py | 4 ++++ 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index f9c21e7f..1a8780de 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ logs/ .env.test .env.production .envrc +*.ini # MCP specific .mcp/ diff --git a/docs/dev_guide.md b/docs/dev_guide.md index 9253b939..84f24217 100644 --- a/docs/dev_guide.md +++ b/docs/dev_guide.md @@ -51,3 +51,12 @@ docker run --rm -it -v "$PWD:/srv/jekyll" jekyll/jekyll:4.2.0 jekyll build ``` The built site will be in `pages/_site/`. + + +## Debug mode for mcpm router +Set environment variable `MCPM_DEBUG` to `true` to enable debug mode. + +```bash +export MCPM_DEBUG=true +``` + diff --git a/src/mcpm/commands/router.py b/src/mcpm/commands/router.py index d4884581..1cad45cb 100644 --- a/src/mcpm/commands/router.py +++ b/src/mcpm/commands/router.py @@ -289,7 +289,8 @@ def router_status(): @click.help_option("-h", "--help") @click.option("-a", "--address", type=str, required=False, help="Remote address to bind the tunnel to") @click.option("-p", "--profile", type=str, required=False, help="Profile to share") -def share(address, profile): +@click.option("-http", type=bool, flag_value=True, required=False, help="Use HTTP instead of HTTPS") +def share(address, profile, http): """Create a share link for the MCPRouter daemon process. Example: @@ -301,6 +302,7 @@ def share(address, profile): # check if there is a router already running pid = read_pid_file() config_manager = ConfigManager() + config = config_manager.get_router_config() if not pid: console.print("[yellow]MCPRouter is not running.[/]") return @@ -324,7 +326,6 @@ def share(address, profile): # get share address if not address: console.print("[cyan]Using share address from config...[/]") - config = config_manager.get_router_config() address = config["share_address"] # create share link @@ -332,14 +333,15 @@ def share(address, profile): # start tunnel # TODO: tls certificate if necessary - tunnel = Tunnel(remote_host, remote_port, config["host"], config["port"], secrets.token_urlsafe(32), None) + tunnel = Tunnel(remote_host, remote_port, config["host"], config["port"], secrets.token_urlsafe(32), http, None) share_url = tunnel.start_tunnel() share_pid = tunnel.proc.pid if tunnel.proc else None # generate random api key api_key = str(uuid.uuid4()) console.print(f"[bold green]Generated secret for share link: {api_key}[/]") - # TODO: https is not supported yet - share_url = share_url.replace("https://", "http://") + "/sse" + if http: + share_url = share_url.replace("https://", "http://") + share_url = share_url + "/sse" # save share pid and link to config config_manager.save_share_config(share_url, share_pid, api_key) profile = profile or "" diff --git a/src/mcpm/router/app.py b/src/mcpm/router/app.py index 24c23459..27862a8e 100644 --- a/src/mcpm/router/app.py +++ b/src/mcpm/router/app.py @@ -24,7 +24,7 @@ CORS_ENABLED = os.environ.get("MCPM_ROUTER_CORS") logging.basicConfig( - level=logging.INFO, + level=logging.INFO if not os.environ.get("MCPM_DEBUG") else logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[logging.FileHandler(LOG_FILE), logging.StreamHandler()], ) @@ -137,7 +137,7 @@ async def lifespan(app): ) app = Starlette( - debug=False, + debug=os.environ.get("MCPM_DEBUG") == "true", middleware=middlewares, routes=[ Route("/sse", endpoint=handle_sse), diff --git a/src/mcpm/router/share.py b/src/mcpm/router/share.py index 7610fd8e..16c95cfc 100644 --- a/src/mcpm/router/share.py +++ b/src/mcpm/router/share.py @@ -64,6 +64,7 @@ def __init__( local_host: str, local_port: int, share_token: str, + http: bool, share_server_tls_certificate: str | None, ): self.proc = None @@ -73,6 +74,7 @@ def __init__( self.local_host = local_host self.local_port = local_port self.share_token = share_token + self.http = http self.share_server_tls_certificate = share_server_tls_certificate @staticmethod @@ -142,6 +144,8 @@ def _start_tunnel(self, binary: str) -> str: self.share_server_tls_certificate, ] ) + if not self.http: + command.append("--tls_enable") self.proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True) return self._read_url_from_tunnel_stream() From aa1d95a69d4adce6b731cbd6e1b640290b097474 Mon Sep 17 00:00:00 2001 From: Jonathan Wang Date: Fri, 25 Apr 2025 13:24:36 +0800 Subject: [PATCH 2/2] chore: fix comment --- src/mcpm/commands/profile.py | 2 +- src/mcpm/commands/router.py | 5 +---- src/mcpm/router/share.py | 2 ++ 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mcpm/commands/profile.py b/src/mcpm/commands/profile.py index 9cb981f6..77152884 100644 --- a/src/mcpm/commands/profile.py +++ b/src/mcpm/commands/profile.py @@ -160,7 +160,7 @@ def add(profile, force=False): profile_config_manager.new_profile(profile) console.print(f"\n[green]Profile '{profile}' added successfully.[/]\n") - console.print(f"You can now add servers to this profile with 'mcpm add --target #{profile} '\n") + console.print(f"You can now add servers to this profile with 'mcpm add --target %{profile} '\n") console.print( f"Or apply existing config to this profile with 'mcpm profile apply {profile} --server '\n" ) diff --git a/src/mcpm/commands/router.py b/src/mcpm/commands/router.py index 1cad45cb..cf2c16a9 100644 --- a/src/mcpm/commands/router.py +++ b/src/mcpm/commands/router.py @@ -289,7 +289,7 @@ def router_status(): @click.help_option("-h", "--help") @click.option("-a", "--address", type=str, required=False, help="Remote address to bind the tunnel to") @click.option("-p", "--profile", type=str, required=False, help="Profile to share") -@click.option("-http", type=bool, flag_value=True, required=False, help="Use HTTP instead of HTTPS") +@click.option("--http", type=bool, flag_value=True, required=False, help="Use HTTP instead of HTTPS") def share(address, profile, http): """Create a share link for the MCPRouter daemon process. @@ -332,15 +332,12 @@ def share(address, profile, http): remote_host, remote_port = address.split(":") # start tunnel - # TODO: tls certificate if necessary tunnel = Tunnel(remote_host, remote_port, config["host"], config["port"], secrets.token_urlsafe(32), http, None) share_url = tunnel.start_tunnel() share_pid = tunnel.proc.pid if tunnel.proc else None # generate random api key api_key = str(uuid.uuid4()) console.print(f"[bold green]Generated secret for share link: {api_key}[/]") - if http: - share_url = share_url.replace("https://", "http://") share_url = share_url + "/sse" # save share pid and link to config config_manager.save_share_config(share_url, share_pid, api_key) diff --git a/src/mcpm/router/share.py b/src/mcpm/router/share.py index 16c95cfc..a4583a23 100644 --- a/src/mcpm/router/share.py +++ b/src/mcpm/router/share.py @@ -186,4 +186,6 @@ def _raise_tunnel_error(): elif "login to server failed" in line: _raise_tunnel_error() + if self.http: + url = url.replace("https://", "http://") return url